debian: apply security fixes from 2.20.2

Apply these as patches instead of using an upstream tarball because
the upstream tarball has not been created yet.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
diff --git a/debian/changelog b/debian/changelog
index ef06a40..fa84381 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,23 @@
+git (1:2.20.1-2+deb10u1) buster-security; urgency=high
+
+  * new upstream point release (see RelNotes/2.20.2.txt).
+    * Addresses the security issues CVE-2019-1348, CVE-2019-1349,
+      CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
+      CVE-2019-1354, and CVE-2019-1387.
+
+      Credit for finding these vulnerabilities goes to Microsoft
+      Security Response Center, in particular to Nicolas Joly. Fixes
+      were provided by Jeff King and Johannes Schindelin with help
+      from Garima Singh.
+
+    * Addresses CVE-2019-19604, arbitrary code execution via the
+      "update" field in .gitmodules.
+
+      Credit for finding this vulnerability goes to Joern
+      Schneeweisz from GitLab.
+
+ -- Jonathan Nieder <jrnieder@gmail.com>  Sun, 08 Dec 2019 22:56:16 -0800
+
 git (1:2.20.1-2) unstable; urgency=low
 
   * package git-gui: actually Suggests: meld for mergetool support;
diff --git a/debian/patches/0001-t9300-drop-some-useless-uses-of-cat.diff b/debian/patches/0001-t9300-drop-some-useless-uses-of-cat.diff
new file mode 100644
index 0000000..22ff286
--- /dev/null
+++ b/debian/patches/0001-t9300-drop-some-useless-uses-of-cat.diff
@@ -0,0 +1,63 @@
+From f19c54aefb8efdd5ace85e44a9e79fdb0a05d641 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 11:19:18 -0400
+Subject: t9300: drop some useless uses of cat
+
+These waste a process, and make the line longer than it needs to be.
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit f94804c1f2626831c6bdf8cc269a571324e3f2f2)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t9300-fast-import.sh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index 59a13b6a77..8621eb3889 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2125,12 +2125,12 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
+ 
+ 	EOF
+ 
+-	cat input | git fast-import &&
++	git fast-import <input &&
+ 	grep :1 git.marks
+ '
+ 
+ test_expect_success 'R: export-marks options can be overridden by commandline options' '
+-	cat input | git fast-import --export-marks=other.marks &&
++	git fast-import --export-marks=other.marks <input &&
+ 	grep :1 other.marks
+ '
+ 
+@@ -2239,7 +2239,7 @@ test_expect_success 'R: import to output marks works without any content' '
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	cat input | git fast-import &&
++	git fast-import <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2249,7 +2249,7 @@ test_expect_success 'R: import marks prefers commandline marks file over the str
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	cat input | git fast-import --import-marks=marks.out &&
++	git fast-import --import-marks=marks.out <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2557,7 +2557,7 @@ test_expect_success 'R: quiet option results in no stats being output' '
+ 
+ 	EOF
+ 
+-	cat input | git fast-import 2> output &&
++	git fast-import 2>output <input &&
+ 	test_must_be_empty output
+ '
+ 
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0002-t9300-create-marks-files-for-double-import-marks-test.diff b/debian/patches/0002-t9300-create-marks-files-for-double-import-marks-test.diff
new file mode 100644
index 0000000..3429abd
--- /dev/null
+++ b/debian/patches/0002-t9300-create-marks-files-for-double-import-marks-test.diff
@@ -0,0 +1,35 @@
+From fb73c3a26454bb024d05f63dd6bd0f11f09bae3d Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 13:43:23 -0400
+Subject: t9300: create marks files for double-import-marks test
+
+Our tests confirm that providing two "import-marks" options in a
+fast-import stream is an error. However, the invoked command would fail
+even without covering this case, because the marks files themselves do
+not actually exist.  Let's create the files to make sure we fail for the
+right reason (we actually do, because the option parsing happens before
+we open anything, but this future-proofs our test).
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit 816f806786e12435163c591942a204c5a3bdd795)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t9300-fast-import.sh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index 8621eb3889..f79fcf4c1e 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2107,6 +2107,8 @@ test_expect_success 'R: abort on receiving feature after data command' '
+ '
+ 
+ test_expect_success 'R: only one import-marks feature allowed per stream' '
++	>git.marks &&
++	>git2.marks &&
+ 	cat >input <<-EOF &&
+ 	feature import-marks=git.marks
+ 	feature import-marks=git2.marks
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0003-fast-import-tighten-parsing-of-boolean-command-line-o.diff b/debian/patches/0003-fast-import-tighten-parsing-of-boolean-command-line-o.diff
new file mode 100644
index 0000000..324b830
--- /dev/null
+++ b/debian/patches/0003-fast-import-tighten-parsing-of-boolean-command-line-o.diff
@@ -0,0 +1,40 @@
+From 1b1b26dda4fe6bb70091a12c5f36d20617df2b1f Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 11:25:45 -0400
+Subject: fast-import: tighten parsing of boolean command line options
+
+We parse options like "--max-pack-size=" using skip_prefix(), which
+makes sense to get at the bytes after the "=". However, we also parse
+"--quiet" and "--stats" with skip_prefix(), which allows things like
+"--quiet-nonsense" to behave like "--quiet".
+
+This was a mistaken conversion in 0f6927c229 (fast-import: put option
+parsing code in separate functions, 2009-12-04). Let's tighten this to
+an exact match, which was the original intent.
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit 11e934d56e46875b24d8a047d44b45ff243f6715)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fast-import.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fast-import.c b/fast-import.c
+index 69886687ce..30b9479a75 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -3282,9 +3282,9 @@ static int parse_one_option(const char *option)
+ 		option_active_branches(option);
+ 	} else if (skip_prefix(option, "export-pack-edges=", &option)) {
+ 		option_export_pack_edges(option);
+-	} else if (starts_with(option, "quiet")) {
++	} else if (!strcmp(option, "quiet")) {
+ 		show_stats = 0;
+-	} else if (starts_with(option, "stats")) {
++	} else if (!strcmp(option, "stats")) {
+ 		show_stats = 1;
+ 	} else {
+ 		return 0;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0004-fast-import-stop-creating-leading-directories-for-imp.diff b/debian/patches/0004-fast-import-stop-creating-leading-directories-for-imp.diff
new file mode 100644
index 0000000..94b8c6c
--- /dev/null
+++ b/debian/patches/0004-fast-import-stop-creating-leading-directories-for-imp.diff
@@ -0,0 +1,41 @@
+From 5fb9612777dbfe7fbc691dfc3d47e9edaa45e013 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 13:07:04 -0400
+Subject: fast-import: stop creating leading directories for import-marks
+
+When asked to import marks from "subdir/file.marks", we create the
+leading directory "subdir" if it doesn't exist. This makes no sense for
+importing marks, where we only ever open the path for reading.
+
+Most of the time this would be a noop, since if the marks file exists,
+then the leading directories exist, too. But if it doesn't (e.g.,
+because --import-marks-if-exists was used), then we'd create the useless
+directory.
+
+This dates back to 580d5f83e7 (fast-import: always create marks_file
+directories, 2010-03-29). Even then it was useless, so it seems to have
+been added in error alongside the --export-marks case (which _is_
+helpful).
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit e075dba3723875f478654068609f69b2a5af8566)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fast-import.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/fast-import.c b/fast-import.c
+index 30b9479a75..b05d560d0a 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -3198,7 +3198,6 @@ static void option_import_marks(const char *marks,
+ 	}
+ 
+ 	import_marks_file = make_fast_import_path(marks);
+-	safe_create_leading_directories_const(import_marks_file);
+ 	import_marks_file_from_stream = from_stream;
+ 	import_marks_file_ignore_missing = ignore_missing;
+ }
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0005-fast-import-delay-creating-leading-directories-for-ex.diff b/debian/patches/0005-fast-import-delay-creating-leading-directories-for-ex.diff
new file mode 100644
index 0000000..a1e6226
--- /dev/null
+++ b/debian/patches/0005-fast-import-delay-creating-leading-directories-for-ex.diff
@@ -0,0 +1,89 @@
+From f4b405ed50ff67d853cb13d3958e662525260cf6 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 13:33:48 -0400
+Subject: fast-import: delay creating leading directories for export-marks
+
+When we parse the --export-marks option, we don't immediately open the
+file, but we do create any leading directories. This can be especially
+confusing when a command-line option overrides an in-stream one, in
+which case we'd create the leading directory for the in-stream file,
+even though we never actually write the file.
+
+Let's instead create the directories just before opening the file, which
+means we'll create only useful directories. Note that this could change
+the handling of relative paths if we chdir() in between, but we don't
+actually do so; the only permanent chdir is from setup_git_directory()
+which runs before either code path (potentially we should take the
+pre-setup dir into account to avoid surprising the user, but that's an
+orthogonal change).
+
+The test just adapts the existing "override" test to use paths with
+leading directories. This checks both that the correct directory is
+created (which worked before but was not tested), and that the
+overridden one is not (our new fix here).
+
+While we're here, let's also check the error result of
+safe_create_leading_directories(). We'd presumably notice any failure
+immediately after when we try to open the file itself, but we can give a
+more specific error message in this case.
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit 019683025f1b14d7cb671312ab01f7330e9b33e7)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fast-import.c          |  7 ++++++-
+ t/t9300-fast-import.sh | 13 +++++++++++--
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/fast-import.c b/fast-import.c
+index b05d560d0a..92e84d28a4 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -1826,6 +1826,12 @@ static void dump_marks(void)
+ 	if (!export_marks_file || (import_marks_file && !import_marks_file_done))
+ 		return;
+ 
++	if (safe_create_leading_directories_const(export_marks_file)) {
++		failure |= error_errno("unable to create leading directories of %s",
++				       export_marks_file);
++		return;
++	}
++
+ 	if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
+ 		failure |= error_errno("Unable to write marks file %s",
+ 				       export_marks_file);
+@@ -3238,7 +3244,6 @@ static void option_active_branches(const char *branches)
+ static void option_export_marks(const char *marks)
+ {
+ 	export_marks_file = make_fast_import_path(marks);
+-	safe_create_leading_directories_const(export_marks_file);
+ }
+ 
+ static void option_cat_blob_fd(const char *fd)
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index f79fcf4c1e..f5f3e2cb71 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2132,8 +2132,17 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
+ '
+ 
+ test_expect_success 'R: export-marks options can be overridden by commandline options' '
+-	git fast-import --export-marks=other.marks <input &&
+-	grep :1 other.marks
++	cat >input <<-\EOF &&
++	feature export-marks=feature-sub/git.marks
++	blob
++	mark :1
++	data 3
++	hi
++
++	EOF
++	git fast-import --export-marks=cmdline-sub/other.marks <input &&
++	grep :1 cmdline-sub/other.marks &&
++	test_path_is_missing feature-sub
+ '
+ 
+ test_expect_success 'R: catch typo in marks file name' '
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0006-fast-import-disallow-feature-export-marks-by-default.diff b/debian/patches/0006-fast-import-disallow-feature-export-marks-by-default.diff
new file mode 100644
index 0000000..9ff56bd
--- /dev/null
+++ b/debian/patches/0006-fast-import-disallow-feature-export-marks-by-default.diff
@@ -0,0 +1,261 @@
+From 6fcb9e67678b3dbab5e12589470407d6f69ca11d Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 14:37:26 -0400
+Subject: fast-import: disallow "feature export-marks" by default
+
+The fast-import stream command "feature export-marks=<path>" lets the
+stream write marks to an arbitrary path. This may be surprising if you
+are running fast-import against an untrusted input (which otherwise
+cannot do anything except update Git objects and refs).
+
+Let's disallow the use of this feature by default, and provide a
+command-line option to re-enable it (you can always just use the
+command-line --export-marks as well, but the in-stream version provides
+an easy way for exporters to control the process).
+
+This is a backwards-incompatible change, since the default is flipping
+to the new, safer behavior. However, since the main users of the
+in-stream versions would be import/export-based remote helpers, and
+since we trust remote helpers already (which are already running
+arbitrary code), we'll pass the new option by default when reading a
+remote helper's stream. This should minimize the impact.
+
+Note that the implementation isn't totally simple, as we have to work
+around the fact that fast-import doesn't parse its command-line options
+until after it has read any "feature" lines from the stream. This is how
+it lets command-line options override in-stream. But in our case, it's
+important to parse the new --allow-unsafe-features first.
+
+There are three options for resolving this:
+
+  1. Do a separate "early" pass over the options. This is easy for us to
+     do because there are no command-line options that allow the
+     "unstuck" form (so there's no chance of us mistaking an argument
+     for an option), though it does introduce a risk of incorrect
+     parsing later (e.g,. if we convert to parse-options).
+
+  2. Move the option parsing phase back to the start of the program, but
+     teach the stream-reading code never to override an existing value.
+     This is tricky, because stream "feature" lines override each other
+     (meaning we'd have to start tracking the source for every option).
+
+  3. Accept that we might parse a "feature export-marks" line that is
+     forbidden, as long we don't _act_ on it until after we've parsed
+     the command line options.
+
+     This would, in fact, work with the current code, but only because
+     the previous patch fixed the export-marks parser to avoid touching
+     the filesystem.
+
+     So while it works, it does carry risk of somebody getting it wrong
+     in the future in a rather subtle and unsafe way.
+
+I've gone with option (1) here as simple, safe, and unlikely to cause
+regressions.
+
+This fixes CVE-2019-1348.
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit 68061e3470210703cb15594194718d35094afdc0)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/git-fast-import.txt | 14 ++++++++++++++
+ fast-import.c                     | 25 +++++++++++++++++++++++++
+ t/t9300-fast-import.sh            | 23 +++++++++++++++--------
+ transport-helper.c                |  1 +
+ 4 files changed, 55 insertions(+), 8 deletions(-)
+
+diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
+index e81117d27f..e90b3b8dff 100644
+--- a/Documentation/git-fast-import.txt
++++ b/Documentation/git-fast-import.txt
+@@ -50,6 +50,20 @@ OPTIONS
+ 	memory used by fast-import during this run.  Showing this output
+ 	is currently the default, but can be disabled with --quiet.
+ 
++--allow-unsafe-features::
++	Many command-line options can be provided as part of the
++	fast-import stream itself by using the `feature` or `option`
++	commands. However, some of these options are unsafe (e.g.,
++	allowing fast-import to access the filesystem outside of the
++	repository). These options are disabled by default, but can be
++	allowed by providing this option on the command line.  This
++	currently impacts only the `feature export-marks` command.
+++
++	Only enable this option if you trust the program generating the
++	fast-import stream! This option is enabled automatically for
++	remote-helpers that use the `import` capability, as they are
++	already trusted to run their own code.
++
+ Options for Frontends
+ ~~~~~~~~~~~~~~~~~~~~~
+ 
+diff --git a/fast-import.c b/fast-import.c
+index 92e84d28a4..e17be6bb54 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -364,6 +364,7 @@ static uintmax_t next_mark;
+ static struct strbuf new_data = STRBUF_INIT;
+ static int seen_data_command;
+ static int require_explicit_termination;
++static int allow_unsafe_features;
+ 
+ /* Signal handling */
+ static volatile sig_atomic_t checkpoint_requested;
+@@ -3290,6 +3291,8 @@ static int parse_one_option(const char *option)
+ 		show_stats = 0;
+ 	} else if (!strcmp(option, "stats")) {
+ 		show_stats = 1;
++	} else if (!strcmp(option, "allow-unsafe-features")) {
++		; /* already handled during early option parsing */
+ 	} else {
+ 		return 0;
+ 	}
+@@ -3297,6 +3300,13 @@ static int parse_one_option(const char *option)
+ 	return 1;
+ }
+ 
++static void check_unsafe_feature(const char *feature, int from_stream)
++{
++	if (from_stream && !allow_unsafe_features)
++		die(_("feature '%s' forbidden in input without --allow-unsafe-features"),
++		    feature);
++}
++
+ static int parse_one_feature(const char *feature, int from_stream)
+ {
+ 	const char *arg;
+@@ -3308,6 +3318,7 @@ static int parse_one_feature(const char *feature, int from_stream)
+ 	} else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
+ 		option_import_marks(arg, from_stream, 1);
+ 	} else if (skip_prefix(feature, "export-marks=", &arg)) {
++		check_unsafe_feature(feature, from_stream);
+ 		option_export_marks(arg);
+ 	} else if (!strcmp(feature, "get-mark")) {
+ 		; /* Don't die - this feature is supported */
+@@ -3434,6 +3445,20 @@ int cmd_main(int argc, const char **argv)
+ 	avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
+ 	marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
+ 
++	/*
++	 * We don't parse most options until after we've seen the set of
++	 * "feature" lines at the start of the stream (which allows the command
++	 * line to override stream data). But we must do an early parse of any
++	 * command-line options that impact how we interpret the feature lines.
++	 */
++	for (i = 1; i < argc; i++) {
++		const char *arg = argv[i];
++		if (*arg != '-' || !strcmp(arg, "--"))
++			break;
++		if (!strcmp(arg, "--allow-unsafe-features"))
++			allow_unsafe_features = 1;
++	}
++
+ 	global_argc = argc;
+ 	global_argv = argv;
+ 
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index f5f3e2cb71..9af3d99eac 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2117,6 +2117,11 @@ test_expect_success 'R: only one import-marks feature allowed per stream' '
+ 	test_must_fail git fast-import <input
+ '
+ 
++test_expect_success 'R: export-marks feature forbidden by default' '
++	echo "feature export-marks=git.marks" >input &&
++	test_must_fail git fast-import <input
++'
++
+ test_expect_success 'R: export-marks feature results in a marks file being created' '
+ 	cat >input <<-EOF &&
+ 	feature export-marks=git.marks
+@@ -2127,7 +2132,7 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
+ 
+ 	EOF
+ 
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	grep :1 git.marks
+ '
+ 
+@@ -2140,7 +2145,8 @@ test_expect_success 'R: export-marks options can be overridden by commandline op
+ 	hi
+ 
+ 	EOF
+-	git fast-import --export-marks=cmdline-sub/other.marks <input &&
++	git fast-import --allow-unsafe-features \
++			--export-marks=cmdline-sub/other.marks <input &&
+ 	grep :1 cmdline-sub/other.marks &&
+ 	test_path_is_missing feature-sub
+ '
+@@ -2148,7 +2154,7 @@ test_expect_success 'R: export-marks options can be overridden by commandline op
+ test_expect_success 'R: catch typo in marks file name' '
+ 	test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
+ 	echo "feature import-marks=nonexistent.marks" |
+-	test_must_fail git fast-import
++	test_must_fail git fast-import --allow-unsafe-features
+ '
+ 
+ test_expect_success 'R: import and output marks can be the same file' '
+@@ -2250,7 +2256,7 @@ test_expect_success 'R: import to output marks works without any content' '
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2260,7 +2266,7 @@ test_expect_success 'R: import marks prefers commandline marks file over the str
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	git fast-import --import-marks=marks.out <input &&
++	git fast-import --import-marks=marks.out --allow-unsafe-features <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2273,7 +2279,8 @@ test_expect_success 'R: multiple --import-marks= should be honoured' '
+ 
+ 	head -n2 marks.out > one.marks &&
+ 	tail -n +3 marks.out > two.marks &&
+-	git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
++	git fast-import --import-marks=one.marks --import-marks=two.marks \
++		--allow-unsafe-features <input &&
+ 	test_cmp marks.out combined.marks
+ '
+ 
+@@ -2286,7 +2293,7 @@ test_expect_success 'R: feature relative-marks should be honoured' '
+ 
+ 	mkdir -p .git/info/fast-import/ &&
+ 	cp marks.new .git/info/fast-import/relative.in &&
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	test_cmp marks.new .git/info/fast-import/relative.out
+ '
+ 
+@@ -2298,7 +2305,7 @@ test_expect_success 'R: feature no-relative-marks should be honoured' '
+ 	feature export-marks=non-relative.out
+ 	EOF
+ 
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	test_cmp marks.new non-relative.out
+ '
+ 
+diff --git a/transport-helper.c b/transport-helper.c
+index bf225c698f..f79221cc94 100644
+--- a/transport-helper.c
++++ b/transport-helper.c
+@@ -423,6 +423,7 @@ static int get_importer(struct transport *transport, struct child_process *fasti
+ 	child_process_init(fastimport);
+ 	fastimport->in = helper->out;
+ 	argv_array_push(&fastimport->args, "fast-import");
++	argv_array_push(&fastimport->args, "--allow-unsafe-features");
+ 	argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
+ 
+ 	if (data->bidi_import) {
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0007-fast-import-disallow-feature-import-marks-by-default.diff b/debian/patches/0007-fast-import-disallow-feature-import-marks-by-default.diff
new file mode 100644
index 0000000..af13e8c
--- /dev/null
+++ b/debian/patches/0007-fast-import-disallow-feature-import-marks-by-default.diff
@@ -0,0 +1,124 @@
+From cbf8dcb9e2fde6fdfc15df4fca678938fe5d473c Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 15:08:42 -0400
+Subject: fast-import: disallow "feature import-marks" by default
+
+As with export-marks in the previous commit, import-marks can access the
+filesystem. This is significantly less dangerous than export-marks
+because it only involves reading from arbitrary paths, rather than
+writing them. However, it could still be surprising and have security
+implications (e.g., exfiltrating data from a service that accepts
+fast-import streams).
+
+Let's lump it (and its "if-exists" counterpart) in with export-marks,
+and enable the in-stream version only if --allow-unsafe-features is set.
+
+Signed-off-by: Jeff King <peff@peff.net>
+(cherry picked from commit a52ed76142f6e8d993bb4c50938a408966eb2b7c)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/git-fast-import.txt |  3 ++-
+ fast-import.c                     |  2 ++
+ t/t9300-fast-import.sh            | 22 +++++++++++++++++-----
+ 3 files changed, 21 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
+index e90b3b8dff..9e3b9581d7 100644
+--- a/Documentation/git-fast-import.txt
++++ b/Documentation/git-fast-import.txt
+@@ -57,7 +57,8 @@ OPTIONS
+ 	allowing fast-import to access the filesystem outside of the
+ 	repository). These options are disabled by default, but can be
+ 	allowed by providing this option on the command line.  This
+-	currently impacts only the `feature export-marks` command.
++	currently impacts only the `export-marks`, `import-marks`, and
++	`import-marks-if-exists` feature commands.
+ +
+ 	Only enable this option if you trust the program generating the
+ 	fast-import stream! This option is enabled automatically for
+diff --git a/fast-import.c b/fast-import.c
+index e17be6bb54..c8b372bc4a 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -3314,8 +3314,10 @@ static int parse_one_feature(const char *feature, int from_stream)
+ 	if (skip_prefix(feature, "date-format=", &arg)) {
+ 		option_date_format(arg);
+ 	} else if (skip_prefix(feature, "import-marks=", &arg)) {
++		check_unsafe_feature("import-marks", from_stream);
+ 		option_import_marks(arg, from_stream, 0);
+ 	} else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
++		check_unsafe_feature("import-marks-if-exists", from_stream);
+ 		option_import_marks(arg, from_stream, 1);
+ 	} else if (skip_prefix(feature, "export-marks=", &arg)) {
+ 		check_unsafe_feature(feature, from_stream);
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index 9af3d99eac..377c2b4958 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2106,6 +2106,14 @@ test_expect_success 'R: abort on receiving feature after data command' '
+ 	test_must_fail git fast-import <input
+ '
+ 
++test_expect_success 'R: import-marks features forbidden by default' '
++	>git.marks &&
++	echo "feature import-marks=git.marks" >input &&
++	test_must_fail git fast-import <input &&
++	echo "feature import-marks-if-exists=git.marks" >input &&
++	test_must_fail git fast-import <input
++'
++
+ test_expect_success 'R: only one import-marks feature allowed per stream' '
+ 	>git.marks &&
+ 	>git2.marks &&
+@@ -2114,7 +2122,7 @@ test_expect_success 'R: only one import-marks feature allowed per stream' '
+ 	feature import-marks=git2.marks
+ 	EOF
+ 
+-	test_must_fail git fast-import <input
++	test_must_fail git fast-import --allow-unsafe-features <input
+ '
+ 
+ test_expect_success 'R: export-marks feature forbidden by default' '
+@@ -2209,7 +2217,8 @@ test_expect_success 'R: --import-marks-if-exists' '
+ test_expect_success 'R: feature import-marks-if-exists' '
+ 	rm -f io.marks &&
+ 
+-	git fast-import --export-marks=io.marks <<-\EOF &&
++	git fast-import --export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=not_io.marks
+ 	EOF
+ 	test_must_be_empty io.marks &&
+@@ -2220,7 +2229,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	echo ":1 $blob" >expect &&
+ 	echo ":2 $blob" >>expect &&
+ 
+-	git fast-import --export-marks=io.marks <<-\EOF &&
++	git fast-import --export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=io.marks
+ 	blob
+ 	mark :2
+@@ -2233,7 +2243,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	echo ":3 $blob" >>expect &&
+ 
+ 	git fast-import --import-marks=io.marks \
+-			--export-marks=io.marks <<-\EOF &&
++			--export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=not_io.marks
+ 	blob
+ 	mark :3
+@@ -2244,7 +2255,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	test_cmp expect io.marks &&
+ 
+ 	git fast-import --import-marks-if-exists=not_io.marks \
+-			--export-marks=io.marks <<-\EOF &&
++			--export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=io.marks
+ 	EOF
+ 	test_must_be_empty io.marks
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0008-clone-recurse-submodules-prevent-name-squatting-on-Wi.diff b/debian/patches/0008-clone-recurse-submodules-prevent-name-squatting-on-Wi.diff
new file mode 100644
index 0000000..2171eaa
--- /dev/null
+++ b/debian/patches/0008-clone-recurse-submodules-prevent-name-squatting-on-Wi.diff
@@ -0,0 +1,238 @@
+From 75a7e32b6754160e38ca8ce372a3849527f59b91 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Thu, 12 Sep 2019 14:20:39 +0200
+Subject: clone --recurse-submodules: prevent name squatting on Windows
+
+In addition to preventing `.git` from being tracked by Git, on Windows
+we also have to prevent `git~1` from being tracked, as the default NTFS
+short name (also known as the "8.3 filename") for the file name `.git`
+is `git~1`, otherwise it would be possible for malicious repositories to
+write directly into the `.git/` directory, e.g. a `post-checkout` hook
+that would then be executed _during_ a recursive clone.
+
+When we implemented appropriate protections in 2b4c6efc821 (read-cache:
+optionally disallow NTFS .git variants, 2014-12-16), we had analyzed
+carefully that the `.git` directory or file would be guaranteed to be
+the first directory entry to be written. Otherwise it would be possible
+e.g. for a file named `..git` to be assigned the short name `git~1` and
+subsequently, the short name generated for `.git` would be `git~2`. Or
+`git~3`. Or even `~9999999` (for a detailed explanation of the lengths
+we have to go to protect `.gitmodules`, see the commit message of
+e7cb0b4455c (is_ntfs_dotgit: match other .git files, 2018-05-11)).
+
+However, by exploiting two issues (that will be addressed in a related
+patch series close by), it is currently possible to clone a submodule
+into a non-empty directory:
+
+- On Windows, file names cannot end in a space or a period (for
+  historical reasons: the period separating the base name from the file
+  extension was not actually written to disk, and the base name/file
+  extension was space-padded to the full 8/3 characters, respectively).
+  Helpfully, when creating a directory under the name, say, `sub.`, that
+  trailing period is trimmed automatically and the actual name on disk
+  is `sub`.
+
+  This means that while Git thinks that the submodule names `sub` and
+  `sub.` are different, they both access `.git/modules/sub/`.
+
+- While the backslash character is a valid file name character on Linux,
+  it is not so on Windows. As Git tries to be cross-platform, it
+  therefore allows backslash characters in the file names stored in tree
+  objects.
+
+  Which means that it is totally possible that a submodule `c` sits next
+  to a file `c\..git`, and on Windows, during recursive clone a file
+  called `..git` will be written into `c/`, of course _before_ the
+  submodule is cloned.
+
+Note that the actual exploit is not quite as simple as having a
+submodule `c` next to a file `c\..git`, as we have to make sure that the
+directory `.git/modules/b` already exists when the submodule is checked
+out, otherwise a different code path is taken in `module_clone()` that
+does _not_ allow a non-empty submodule directory to exist already.
+
+Even if we will address both issues nearby (the next commit will
+disallow backslash characters in tree entries' file names on Windows,
+and another patch will disallow creating directories/files with trailing
+spaces or periods), it is a wise idea to defend in depth against this
+sort of attack vector: when submodules are cloned recursively, we now
+_require_ the directory to be empty, addressing CVE-2019-1349.
+
+Note: the code path we patch is shared with the code path of `git
+submodule update --init`, which must not expect, in general, that the
+directory is empty. Hence we have to introduce the new option
+`--force-init` and hand it all the way down from `git submodule` to the
+actual `git submodule--helper` process that performs the initial clone.
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 0060fd1511b94c918928fa3708f69a3f33895a4a)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ builtin/clone.c             |  2 +-
+ builtin/submodule--helper.c | 14 ++++++++++++--
+ git-submodule.sh            |  6 ++++++
+ t/t7415-submodule-names.sh  | 31 +++++++++++++++++++++++++++++++
+ 4 files changed, 50 insertions(+), 3 deletions(-)
+
+diff --git a/builtin/clone.c b/builtin/clone.c
+index 15b142d646..5d31bea55e 100644
+--- a/builtin/clone.c
++++ b/builtin/clone.c
+@@ -768,7 +768,7 @@ static int checkout(int submodule_progress)
+ 
+ 	if (!err && (option_recurse_submodules.nr > 0)) {
+ 		struct argv_array args = ARGV_ARRAY_INIT;
+-		argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
++		argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
+ 
+ 		if (option_shallow_submodules == 1)
+ 			argv_array_push(&args, "--depth=1");
+diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
+index d38113a31a..e103abbcbc 100644
+--- a/builtin/submodule--helper.c
++++ b/builtin/submodule--helper.c
+@@ -18,6 +18,7 @@
+ #include "diffcore.h"
+ #include "diff.h"
+ #include "object-store.h"
++#include "dir.h"
+ 
+ #define OPT_QUIET (1 << 0)
+ #define OPT_CACHED (1 << 1)
+@@ -1354,7 +1355,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 	char *p, *path = NULL, *sm_gitdir;
+ 	struct strbuf sb = STRBUF_INIT;
+ 	struct string_list reference = STRING_LIST_INIT_NODUP;
+-	int dissociate = 0;
++	int dissociate = 0, require_init = 0;
+ 	char *sm_alternate = NULL, *error_strategy = NULL;
+ 
+ 	struct option module_clone_options[] = {
+@@ -1381,6 +1382,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 		OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
+ 		OPT_BOOL(0, "progress", &progress,
+ 			   N_("force cloning progress")),
++		OPT_BOOL(0, "require-init", &require_init,
++			   N_("disallow cloning into non-empty directory")),
+ 		OPT_END()
+ 	};
+ 
+@@ -1419,6 +1422,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 			die(_("clone of '%s' into submodule path '%s' failed"),
+ 			    url, path);
+ 	} else {
++		if (require_init && !access(path, X_OK) && !is_empty_dir(path))
++			die(_("directory not empty: '%s'"), path);
+ 		if (safe_create_leading_directories_const(path) < 0)
+ 			die(_("could not create directory '%s'"), path);
+ 		strbuf_addf(&sb, "%s/index", sm_gitdir);
+@@ -1531,6 +1536,7 @@ struct submodule_update_clone {
+ 	int recommend_shallow;
+ 	struct string_list references;
+ 	int dissociate;
++	unsigned require_init;
+ 	const char *depth;
+ 	const char *recursive_prefix;
+ 	const char *prefix;
+@@ -1549,7 +1555,7 @@ struct submodule_update_clone {
+ 	int max_jobs;
+ };
+ #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
+-	SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
++	SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, 0, \
+ 	NULL, NULL, NULL, \
+ 	NULL, 0, 0, 0, NULL, 0, 0, 0}
+ 
+@@ -1676,6 +1682,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
+ 		argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+ 	if (suc->recommend_shallow && sub->recommend_shallow == 1)
+ 		argv_array_push(&child->args, "--depth=1");
++	if (suc->require_init)
++		argv_array_push(&child->args, "--require-init");
+ 	argv_array_pushl(&child->args, "--path", sub->path, NULL);
+ 	argv_array_pushl(&child->args, "--name", sub->name, NULL);
+ 	argv_array_pushl(&child->args, "--url", url, NULL);
+@@ -1866,6 +1874,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
+ 		OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+ 		OPT_BOOL(0, "progress", &suc.progress,
+ 			    N_("force cloning progress")),
++		OPT_BOOL(0, "require-init", &suc.require_init,
++			   N_("disallow cloning into non-empty directory")),
+ 		OPT_END()
+ 	};
+ 
+diff --git a/git-submodule.sh b/git-submodule.sh
+index 5e608f8bad..65d62c888c 100755
+--- a/git-submodule.sh
++++ b/git-submodule.sh
+@@ -34,6 +34,7 @@ reference=
+ cached=
+ recursive=
+ init=
++require_init=
+ files=
+ remote=
+ nofetch=
+@@ -457,6 +458,10 @@ cmd_update()
+ 		-i|--init)
+ 			init=1
+ 			;;
++		--require-init)
++			init=1
++			require_init=1
++			;;
+ 		--remote)
+ 			remote=1
+ 			;;
+@@ -539,6 +544,7 @@ cmd_update()
+ 		${reference:+"$reference"} \
+ 		${dissociate:+"--dissociate"} \
+ 		${depth:+--depth "$depth"} \
++		${require_init:+--require-init} \
+ 		$recommend_shallow \
+ 		$jobs \
+ 		"$@" || echo "#unmatched" $?
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index 293e2e1963..c9369b10c7 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -191,4 +191,35 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
+ 	)
+ '
+ 
++test_expect_success MINGW 'prevent git~1 squatting on Windows' '
++	git init squatting &&
++	(
++		cd squatting &&
++		mkdir a &&
++		touch a/..git &&
++		git add a/..git &&
++		test_tick &&
++		git commit -m initial &&
++
++		modules="$(test_write_lines \
++			"[submodule \"b.\"]" "url = ." "path = c" \
++			"[submodule \"b\"]" "url = ." "path = d\\\\a" |
++			git hash-object -w --stdin)" &&
++		rev="$(git rev-parse --verify HEAD)" &&
++		hash="$(echo x | git hash-object -w --stdin)" &&
++		git update-index --add \
++			--cacheinfo 100644,$modules,.gitmodules \
++			--cacheinfo 160000,$rev,c \
++			--cacheinfo 160000,$rev,d\\a \
++			--cacheinfo 100644,$hash,d./a/x \
++			--cacheinfo 100644,$hash,d./a/..git &&
++		test_tick &&
++		git commit -m "module"
++	) &&
++	test_must_fail git \
++		clone --recurse-submodules squatting squatting-clone 2>err &&
++	test_i18ngrep "directory not empty" err &&
++	! grep gitdir squatting-clone/d/a/git~2
++'
++
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0009-mingw-disallow-backslash-characters-in-tree-objects-f.diff b/debian/patches/0009-mingw-disallow-backslash-characters-in-tree-objects-f.diff
new file mode 100644
index 0000000..03c41bf
--- /dev/null
+++ b/debian/patches/0009-mingw-disallow-backslash-characters-in-tree-objects-f.diff
@@ -0,0 +1,106 @@
+From 1256ea63660648bd144e8260bc0effb3cd3ee10c Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Thu, 12 Sep 2019 14:54:05 +0200
+Subject: mingw: disallow backslash characters in tree objects' file names
+
+The backslash character is not a valid part of a file name on Windows.
+Hence it is dangerous to allow writing files that were unpacked from
+tree objects, when the stored file name contains a backslash character:
+it will be misinterpreted as directory separator.
+
+This not only causes ambiguity when a tree contains a blob `a\b` and a
+tree `a` that contains a blob `b`, but it also can be used as part of an
+attack vector to side-step the careful protections against writing into
+the `.git/` directory during a clone of a maliciously-crafted
+repository.
+
+Let's prevent that, addressing CVE-2019-1354.
+
+Note: we guard against backslash characters in tree objects' file names
+_only_ on Windows (because on other platforms, even on those where NTFS
+volumes can be mounted, the backslash character is _not_ a directory
+separator), and _only_ when `core.protectNTFS = true` (because users
+might need to generate tree objects for other platforms, of course
+without touching the worktree, e.g. using `git update-index
+--cacheinfo`).
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit e1d911dd4c7b76a5a8cec0f5c8de15981e34da83)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t1450-fsck.sh            | 1 +
+ t/t7415-submodule-names.sh | 8 +++++---
+ t/t9350-fast-export.sh     | 1 +
+ tree-walk.c                | 6 ++++++
+ 4 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
+index e20e8fa830..c767e2783b 100755
+--- a/t/t1450-fsck.sh
++++ b/t/t1450-fsck.sh
+@@ -453,6 +453,7 @@ while read name path pretty; do
+ 		(
+ 			git init $name-$type &&
+ 			cd $name-$type &&
++			git config core.protectNTFS false &&
+ 			echo content >file &&
+ 			git add file &&
+ 			git commit -m base &&
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index c9369b10c7..9b69aed48e 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -207,16 +207,18 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
+ 			git hash-object -w --stdin)" &&
+ 		rev="$(git rev-parse --verify HEAD)" &&
+ 		hash="$(echo x | git hash-object -w --stdin)" &&
+-		git update-index --add \
++		git -c core.protectNTFS=false update-index --add \
+ 			--cacheinfo 100644,$modules,.gitmodules \
+ 			--cacheinfo 160000,$rev,c \
+ 			--cacheinfo 160000,$rev,d\\a \
+ 			--cacheinfo 100644,$hash,d./a/x \
+ 			--cacheinfo 100644,$hash,d./a/..git &&
+ 		test_tick &&
+-		git commit -m "module"
++		git -c core.protectNTFS=false commit -m "module" &&
++		test_must_fail git show HEAD: 2>err &&
++		test_i18ngrep backslash err
+ 	) &&
+-	test_must_fail git \
++	test_must_fail git -c core.protectNTFS=false \
+ 		clone --recurse-submodules squatting squatting-clone 2>err &&
+ 	test_i18ngrep "directory not empty" err &&
+ 	! grep gitdir squatting-clone/d/a/git~2
+diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
+index 6a392e87bc..34124be778 100755
+--- a/t/t9350-fast-export.sh
++++ b/t/t9350-fast-export.sh
+@@ -418,6 +418,7 @@ test_expect_success 'directory becomes symlink'        '
+ 
+ test_expect_success 'fast-export quotes pathnames' '
+ 	git init crazy-paths &&
++	test_config -C crazy-paths core.protectNTFS false &&
+ 	(cd crazy-paths &&
+ 	 blob=$(echo foo | git hash-object -w --stdin) &&
+ 	 git update-index --add \
+diff --git a/tree-walk.c b/tree-walk.c
+index 79bafbd1a2..bf07946ec4 100644
+--- a/tree-walk.c
++++ b/tree-walk.c
+@@ -43,6 +43,12 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
+ 		strbuf_addstr(err, _("empty filename in tree entry"));
+ 		return -1;
+ 	}
++#ifdef GIT_WINDOWS_NATIVE
++	if (protect_ntfs && strchr(path, '\\')) {
++		strbuf_addf(err, _("filename in tree entry contains backslash: '%s'"), path);
++		return -1;
++	}
++#endif
+ 	len = strlen(path) + 1;
+ 
+ 	/* Initialize the descriptor entry */
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0010-path.c-document-the-purpose-of-is_ntfs_dotgit.diff b/debian/patches/0010-path.c-document-the-purpose-of-is_ntfs_dotgit.diff
new file mode 100644
index 0000000..67a217a
--- /dev/null
+++ b/debian/patches/0010-path.c-document-the-purpose-of-is_ntfs_dotgit.diff
@@ -0,0 +1,58 @@
+From 52c5310a07658b27729c7a2f6b076736f0fb8397 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 16 Sep 2019 20:44:31 +0200
+Subject: path.c: document the purpose of `is_ntfs_dotgit()`
+
+Previously, this function was completely undocumented. It is worth,
+though, to explain what is going on, as it is not really obvious at all.
+
+Suggested-by: Garima Singh <garima.singh@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 525e7fba7854c23ee3530d0bf88d75f106f14c95)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/path.c b/path.c
+index dc3294c71e..943b5802f0 100644
+--- a/path.c
++++ b/path.c
+@@ -1306,6 +1306,34 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+ 	return 1;
+ }
+ 
++/*
++ * On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
++ * directory:
++ *
++ * - For historical reasons, file names that end in spaces or periods are
++ *   automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
++ *   to `.git/`.
++ *
++ * - For other historical reasons, file names that do not conform to the 8.3
++ *   format (up to eight characters for the basename, three for the file
++ *   extension, certain characters not allowed such as `+`, etc) are associated
++ *   with a so-called "short name", at least on the `C:` drive by default.
++ *   Which means that `git~1/` is a valid way to refer to `.git/`.
++ *
++ *   Note: Technically, `.git/` could receive the short name `git~2` if the
++ *   short name `git~1` were already used. In Git, however, we guarantee that
++ *   `.git` is the first item in a directory, therefore it will be associated
++ *   with the short name `git~1` (unless short names are disabled).
++ *
++ * When this function returns 1, it indicates that the specified file/directory
++ * name refers to a `.git` file or directory, or to any of these synonyms, and
++ * Git should therefore not track it.
++ *
++ * This function is intended to be used by `git fsck` even on platforms where
++ * the backslash is a regular filename character, therefore it needs to handle
++ * backlash characters in the provided `name` specially: they are interpreted
++ * as directory separators.
++ */
+ int is_ntfs_dotgit(const char *name)
+ {
+ 	size_t len;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0011-test-path-utils-offer-to-run-a-protectNTFS-protectHFS.diff b/debian/patches/0011-test-path-utils-offer-to-run-a-protectNTFS-protectHFS.diff
new file mode 100644
index 0000000..0902f40
--- /dev/null
+++ b/debian/patches/0011-test-path-utils-offer-to-run-a-protectNTFS-protectHFS.diff
@@ -0,0 +1,150 @@
+From 711727ad9226c070554e3b5ff4e020a66bc86d0c Mon Sep 17 00:00:00 2001
+From: Garima Singh <garima.singh@microsoft.com>
+Date: Wed, 4 Sep 2019 13:36:39 -0400
+Subject: test-path-utils: offer to run a protectNTFS/protectHFS benchmark
+
+In preparation to flipping the default on `core.protectNTFS`, let's have
+some way to measure the speed impact of this config setting reliably
+(and for comparison, the `core.protectHFS` config setting).
+
+For now, this is a manual performance benchmark:
+
+	./t/helper/test-path-utils protect_ntfs_hfs [arguments...]
+
+where the arguments are an optional number of file names to test with,
+optionally followed by minimum and maximum length of the random file
+names. The default values are one million, 3 and 20, respectively.
+
+Just like `sqrti()` in `bisect.c`, we introduce a very simple function
+to approximation the square root of a given value, in order to avoid
+having to introduce the first user of `<math.h>` in Git's source code.
+
+Note: this is _not_ implemented as a Unix shell script in t/perf/
+because we really care about _very_ precise timings here, and Unix shell
+scripts are simply unsuited for precise and consistent benchmarking.
+
+Signed-off-by: Garima Singh <garima.singh@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit a62f9d1ace8c6556cbc1bb7df69eff0a0bb9e774)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/helper/test-path-utils.c | 96 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 96 insertions(+)
+
+diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
+index ae091d9b3e..d9411032d2 100644
+--- a/t/helper/test-path-utils.c
++++ b/t/helper/test-path-utils.c
+@@ -177,6 +177,99 @@ static int is_dotgitmodules(const char *path)
+ 	return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
+ }
+ 
++/*
++ * A very simple, reproducible pseudo-random generator. Copied from
++ * `test-genrandom.c`.
++ */
++static uint64_t my_random_value = 1234;
++
++static uint64_t my_random(void)
++{
++	my_random_value = my_random_value * 1103515245 + 12345;
++	return my_random_value;
++}
++
++/*
++ * A fast approximation of the square root, without requiring math.h.
++ *
++ * It uses Newton's method to approximate the solution of 0 = x^2 - value.
++ */
++static double my_sqrt(double value)
++{
++	const double epsilon = 1e-6;
++	double x = value;
++
++	if (value == 0)
++		return 0;
++
++	for (;;) {
++		double delta = (value / x - x) / 2;
++		if (delta < epsilon && delta > -epsilon)
++			return x + delta;
++		x += delta;
++	}
++}
++
++static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
++{
++	size_t i, j, nr, min_len = 3, max_len = 20;
++	char **names;
++	int repetitions = 15, file_mode = 0100644;
++	uint64_t begin, end;
++	double m[3][2], v[3][2];
++	uint64_t cumul;
++	double cumul2;
++
++	if (argc > 1 && !strcmp(argv[1], "--with-symlink-mode")) {
++		file_mode = 0120000;
++		argc--;
++		argv++;
++	}
++
++	nr = argc > 1 ? strtoul(argv[1], NULL, 0) : 1000000;
++	ALLOC_ARRAY(names, nr);
++
++	if (argc > 2) {
++		min_len = strtoul(argv[2], NULL, 0);
++		if (argc > 3)
++			max_len = strtoul(argv[3], NULL, 0);
++		if (min_len > max_len)
++			die("min_len > max_len");
++	}
++
++	for (i = 0; i < nr; i++) {
++		size_t len = min_len + (my_random() % (max_len + 1 - min_len));
++
++		names[i] = xmallocz(len);
++		while (len > 0)
++			names[i][--len] = (char)(' ' + (my_random() % ('\x7f' - ' ')));
++	}
++
++	for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
++		for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) {
++			cumul = 0;
++			cumul2 = 0;
++			for (i = 0; i < repetitions; i++) {
++				begin = getnanotime();
++				for (j = 0; j < nr; j++)
++					verify_path(names[j], file_mode);
++				end = getnanotime();
++				printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", protect_ntfs, protect_hfs, (end-begin) / (double)1e6);
++				cumul += end - begin;
++				cumul2 += (end - begin) * (end - begin);
++			}
++			m[protect_ntfs][protect_hfs] = cumul / (double)repetitions;
++			v[protect_ntfs][protect_hfs] = my_sqrt(cumul2 / (double)repetitions - m[protect_ntfs][protect_hfs] * m[protect_ntfs][protect_hfs]);
++			printf("mean: %lfms, stddev: %lfms\n", m[protect_ntfs][protect_hfs] / (double)1e6, v[protect_ntfs][protect_hfs] / (double)1e6);
++		}
++
++	for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
++		for (protect_hfs = 0; protect_hfs < 2; protect_hfs++)
++			printf("ntfs=%d/hfs=%d: %lf%% slower\n", protect_ntfs, protect_hfs, (m[protect_ntfs][protect_hfs] - m[0][0]) * 100 / m[0][0]);
++
++	return 0;
++}
++
+ int cmd__path_utils(int argc, const char **argv)
+ {
+ 	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
+@@ -291,6 +384,9 @@ int cmd__path_utils(int argc, const char **argv)
+ 		return !!res;
+ 	}
+ 
++	if (argc > 1 && !strcmp(argv[1], "protect_ntfs_hfs"))
++		return !!protect_ntfs_hfs_benchmark(argc - 1, argv + 1);
++
+ 	fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+ 		argv[1] ? argv[1] : "(there was none)");
+ 	return 1;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0012-is_ntfs_dotgit-only-verify-the-leading-segment.diff b/debian/patches/0012-is_ntfs_dotgit-only-verify-the-leading-segment.diff
new file mode 100644
index 0000000..52bbf2f
--- /dev/null
+++ b/debian/patches/0012-is_ntfs_dotgit-only-verify-the-leading-segment.diff
@@ -0,0 +1,143 @@
+From ba45605d7914147e8c441aca37f62758205add04 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 23 Sep 2019 08:58:11 +0200
+Subject: is_ntfs_dotgit(): only verify the leading segment
+
+The config setting `core.protectNTFS` is specifically designed to work
+not only on Windows, but anywhere, to allow for repositories hosted on,
+say, Linux servers to be protected against NTFS-specific attack vectors.
+
+As a consequence, `is_ntfs_dotgit()` manually splits backslash-separated
+paths (but does not do the same for paths separated by forward slashes),
+under the assumption that the backslash might not be a valid directory
+separator on the _current_ Operating System.
+
+However, the two callers, `verify_path()` and `fsck_tree()`, are
+supposed to feed only individual path segments to the `is_ntfs_dotgit()`
+function.
+
+This causes a lot of duplicate scanning (and very inefficient scanning,
+too, as the inner loop of `is_ntfs_dotgit()` was optimized for
+readability rather than for speed.
+
+Let's simplify the design of `is_ntfs_dotgit()` by putting the burden of
+splitting the paths by backslashes as directory separators on the
+callers of said function.
+
+Consequently, the `verify_path()` function, which already splits the
+path by directory separators, now treats backslashes as directory
+separators _explicitly_ when `core.protectNTFS` is turned on, even on
+platforms where the backslash is _not_ a directory separator.
+
+Note that we have to repeat some code in `verify_path()`: if the
+backslash is not a directory separator on the current Operating System,
+we want to allow file names like `\`, but we _do_ want to disallow paths
+that are clearly intended to cause harm when the repository is cloned on
+Windows.
+
+The `fsck_tree()` function (the other caller of `is_ntfs_dotgit()`) now
+needs to look for backslashes in tree entries' names specifically when
+`core.protectNTFS` is turned on. While it would be tempting to
+completely disallow backslashes in that case (much like `fsck` reports
+names containing forward slashes as "full paths"), this would be
+overzealous: when `core.protectNTFS` is turned on in a non-Windows
+setup, backslashes are perfectly valid characters in file names while we
+_still_ want to disallow tree entries that are clearly designed to
+exploit NTFS-specific behavior.
+
+This simplification will make subsequent changes easier to implement,
+such as turning `core.protectNTFS` on by default (not only on Windows)
+or protecting against attack vectors involving NTFS Alternate Data
+Streams.
+
+Incidentally, this change allows for catching malicious repositories
+that contain tree entries of the form `dir\.gitmodules` already on the
+server side rather than only on the client side (and previously only on
+Windows): in contrast to `is_ntfs_dotgit()`, the
+`is_ntfs_dotgitmodules()` function already expects the caller to split
+the paths by directory separators.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 288a74bcd28229a00c3632f18cba92dbfdf73ee9)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fsck.c       | 18 +++++++++++++++++-
+ path.c       |  5 +----
+ read-cache.c |  8 ++++++++
+ 3 files changed, 26 insertions(+), 5 deletions(-)
+
+diff --git a/fsck.c b/fsck.c
+index 68502ce85b..b0526dd2b6 100644
+--- a/fsck.c
++++ b/fsck.c
+@@ -605,7 +605,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
+ 
+ 	while (desc.size) {
+ 		unsigned mode;
+-		const char *name;
++		const char *name, *backslash;
+ 		const struct object_id *oid;
+ 
+ 		oid = tree_entry_extract(&desc, &name, &mode);
+@@ -627,6 +627,22 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
+ 						 ".gitmodules is a symbolic link");
+ 		}
+ 
++		if ((backslash = strchr(name, '\\'))) {
++			while (backslash) {
++				backslash++;
++				has_dotgit |= is_ntfs_dotgit(backslash);
++				if (is_ntfs_dotgitmodules(backslash)) {
++					if (!S_ISLNK(mode))
++						oidset_insert(&gitmodules_found, oid);
++					else
++						retval += report(options, &item->object,
++								 FSCK_MSG_GITMODULES_SYMLINK,
++								 ".gitmodules is a symbolic link");
++				}
++				backslash = strchr(backslash, '\\');
++			}
++		}
++
+ 		if (update_tree_entry_gently(&desc)) {
+ 			retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
+ 			break;
+diff --git a/path.c b/path.c
+index 943b5802f0..2f4f104a1e 100644
+--- a/path.c
++++ b/path.c
+@@ -1346,10 +1346,7 @@ int is_ntfs_dotgit(const char *name)
+ 			if (only_spaces_and_periods(name, len, 5) &&
+ 					!strncasecmp(name, "git~1", 5))
+ 				return 1;
+-			if (name[len] != '\\')
+-				return 0;
+-			name += len + 1;
+-			len = -1;
++			return 0;
+ 		}
+ }
+ 
+diff --git a/read-cache.c b/read-cache.c
+index bd45dc3e24..1a4e184478 100644
+--- a/read-cache.c
++++ b/read-cache.c
+@@ -982,7 +982,15 @@ int verify_path(const char *path, unsigned mode)
+ 			if ((c == '.' && !verify_dotfile(path, mode)) ||
+ 			    is_dir_sep(c) || c == '\0')
+ 				return 0;
++		} else if (c == '\\' && protect_ntfs) {
++			if (is_ntfs_dotgit(path))
++				return 0;
++			if (S_ISLNK(mode)) {
++				if (is_ntfs_dotgitmodules(path))
++					return 0;
++			}
+ 		}
++
+ 		c = *path++;
+ 	}
+ }
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0013-path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff b/debian/patches/0013-path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff
new file mode 100644
index 0000000..d5091d8
--- /dev/null
+++ b/debian/patches/0013-path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff
@@ -0,0 +1,95 @@
+From cebf4898e0e55efaceab09e8bfb1cc362fed53a5 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 28 Aug 2019 12:22:17 +0200
+Subject: path: safeguard `.git` against NTFS Alternate Streams Accesses
+
+Probably inspired by HFS' resource streams, NTFS supports "Alternate
+Data Streams": by appending `:<stream-name>` to the file name,
+information in addition to the file contents can be written and read,
+information that is copied together with the file (unless copied to a
+non-NTFS location).
+
+These Alternate Data Streams are typically used for things like marking
+an executable as having just been downloaded from the internet (and
+hence not necessarily being trustworthy).
+
+In addition to a stream name, a stream type can be appended, like so:
+`:<stream-name>:<stream-type>`. Unless specified, the default stream
+type is `$DATA` for files and `$INDEX_ALLOCATION` for directories. In
+other words, `.git::$INDEX_ALLOCATION` is a valid way to reference the
+`.git` directory!
+
+In our work in Git v2.2.1 to protect Git on NTFS drives under
+`core.protectNTFS`, we focused exclusively on NTFS short names, unaware
+of the fact that NTFS Alternate Data Streams offer a similar attack
+vector.
+
+Let's fix this.
+
+Seeing as it is better to be safe than sorry, we simply disallow paths
+referring to *any* NTFS Alternate Data Stream of `.git`, not just
+`::$INDEX_ALLOCATION`. This also simplifies the implementation.
+
+This closes CVE-2019-1352.
+
+Further reading about NTFS Alternate Data Streams:
+https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c54dec26-1551-4d3a-a0ea-4fa40f848eb3
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 7c3745fc6185495d5765628b4dfe1bd2c25a2981)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c                         | 12 +++++++++++-
+ t/t1014-read-tree-confusing.sh |  1 +
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/path.c b/path.c
+index 2f4f104a1e..02d5dd42f6 100644
+--- a/path.c
++++ b/path.c
+@@ -1325,10 +1325,19 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+  *   `.git` is the first item in a directory, therefore it will be associated
+  *   with the short name `git~1` (unless short names are disabled).
+  *
++ * - For yet other historical reasons, NTFS supports so-called "Alternate Data
++ *   Streams", i.e. metadata associated with a given file, referred to via
++ *   `<filename>:<stream-name>:<stream-type>`. There exists a default stream
++ *   type for directories, allowing `.git/` to be accessed via
++ *   `.git::$INDEX_ALLOCATION/`.
++ *
+  * When this function returns 1, it indicates that the specified file/directory
+  * name refers to a `.git` file or directory, or to any of these synonyms, and
+  * Git should therefore not track it.
+  *
++ * For performance reasons, _all_ Alternate Data Streams of `.git/` are
++ * forbidden, not just `::$INDEX_ALLOCATION`.
++ *
+  * This function is intended to be used by `git fsck` even on platforms where
+  * the backslash is a regular filename character, therefore it needs to handle
+  * backlash characters in the provided `name` specially: they are interpreted
+@@ -1339,7 +1348,8 @@ int is_ntfs_dotgit(const char *name)
+ 	size_t len;
+ 
+ 	for (len = 0; ; len++)
+-		if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
++		if (!name[len] || name[len] == '\\' || is_dir_sep(name[len]) ||
++		    name[len] == ':') {
+ 			if (only_spaces_and_periods(name, len, 4) &&
+ 					!strncasecmp(name, ".git", 4))
+ 				return 1;
+diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh
+index 2f5a25d503..da3376b3bb 100755
+--- a/t/t1014-read-tree-confusing.sh
++++ b/t/t1014-read-tree-confusing.sh
+@@ -49,6 +49,7 @@ git~1
+ .git.SPACE .git.{space}
+ .\\\\.GIT\\\\foobar backslashes
+ .git\\\\foobar backslashes2
++.git...:alternate-stream
+ EOF
+ 
+ test_expect_success 'utf-8 paths allowed with core.protectHFS off' '
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0014-path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff b/debian/patches/0014-path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff
new file mode 100644
index 0000000..ef31a45
--- /dev/null
+++ b/debian/patches/0014-path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff
@@ -0,0 +1,63 @@
+From c90e2540be153209cf71afc18eabe3d42ddac0ca Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 28 Aug 2019 12:22:17 +0200
+Subject: path: also guard `.gitmodules` against NTFS Alternate Data Streams
+
+We just safe-guarded `.git` against NTFS Alternate Data Stream-related
+attack vectors, and now it is time to do the same for `.gitmodules`.
+
+Note: In the added regression test, we refrain from verifying all kinds
+of variations between short names and NTFS Alternate Data Streams: as
+the new code disallows _all_ Alternate Data Streams of `.gitmodules`, it
+is enough to test one in order to know that all of them are guarded
+against.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 91bd46588e6959e6903e275f78b10bd07830d547)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c                | 2 +-
+ t/t0060-path-utils.sh | 7 ++++++-
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/path.c b/path.c
+index 02d5dd42f6..944ef2ca38 100644
+--- a/path.c
++++ b/path.c
+@@ -1373,7 +1373,7 @@ static int is_ntfs_dot_generic(const char *name,
+ only_spaces_and_periods:
+ 		for (;;) {
+ 			char c = name[i++];
+-			if (!c)
++			if (!c || c == ':')
+ 				return 1;
+ 			if (c != ' ' && c != '.')
+ 				return 0;
+diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
+index c7b53e494b..85cccc655a 100755
+--- a/t/t0060-path-utils.sh
++++ b/t/t0060-path-utils.sh
+@@ -423,6 +423,9 @@ test_expect_success 'match .gitmodules' '
+ 		~1000000 \
+ 		~9999999 \
+ 		\
++		.gitmodules:\$DATA \
++		"gitmod~4 . :\$DATA" \
++		\
+ 		--not \
+ 		".gitmodules x"  \
+ 		".gitmodules .x" \
+@@ -447,7 +450,9 @@ test_expect_success 'match .gitmodules' '
+ 		\
+ 		GI7EB~1 \
+ 		GI7EB~01 \
+-		GI7EB~1X
++		GI7EB~1X \
++		\
++		.gitmodules,:\$DATA
+ '
+ 
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0015-is_ntfs_dotgit-speed-it-up.diff b/debian/patches/0015-is_ntfs_dotgit-speed-it-up.diff
new file mode 100644
index 0000000..61b574a
--- /dev/null
+++ b/debian/patches/0015-is_ntfs_dotgit-speed-it-up.diff
@@ -0,0 +1,113 @@
+From 3efd6a6a32f5a990d533446b4ea0be6f959c5209 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Fri, 6 Sep 2019 21:09:35 +0200
+Subject: is_ntfs_dotgit(): speed it up
+
+Previously, this function was written without focusing on speed,
+intending to make reviewing the code as easy as possible, to avoid any
+bugs in this critical code.
+
+Turns out: we can do much better on both accounts. With this patch, we
+make it as fast as this developer can make it go:
+
+- We avoid the call to `is_dir_sep()` and make all the character
+  comparisons explicit.
+
+- We avoid the cost of calling `strncasecmp()` and unroll the test for
+  `.git` and `git~1`, not even using `tolower()` because it is faster to
+  compare against two constant values.
+
+- We look for `.git` and `.git~1` first thing, and return early if not
+  found.
+
+- We also avoid calling a separate function for detecting chains of
+  spaces and periods.
+
+Each of these improvements has a noticeable impact on the speed of
+`is_ntfs_dotgit()`.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 3a85dc7d534fc2d410ddc0c771c963b20d1b4857)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c | 55 ++++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 30 insertions(+), 25 deletions(-)
+
+diff --git a/path.c b/path.c
+index 944ef2ca38..617545cd5d 100644
+--- a/path.c
++++ b/path.c
+@@ -1292,20 +1292,6 @@ int daemon_avoid_alias(const char *p)
+ 	}
+ }
+ 
+-static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+-{
+-	if (len < skip)
+-		return 0;
+-	len -= skip;
+-	path += skip;
+-	while (len-- > 0) {
+-		char c = *(path++);
+-		if (c != ' ' && c != '.')
+-			return 0;
+-	}
+-	return 1;
+-}
+-
+ /*
+  * On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
+  * directory:
+@@ -1345,19 +1331,38 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+  */
+ int is_ntfs_dotgit(const char *name)
+ {
+-	size_t len;
++	char c;
+ 
+-	for (len = 0; ; len++)
+-		if (!name[len] || name[len] == '\\' || is_dir_sep(name[len]) ||
+-		    name[len] == ':') {
+-			if (only_spaces_and_periods(name, len, 4) &&
+-					!strncasecmp(name, ".git", 4))
+-				return 1;
+-			if (only_spaces_and_periods(name, len, 5) &&
+-					!strncasecmp(name, "git~1", 5))
+-				return 1;
++	/*
++	 * Note that when we don't find `.git` or `git~1` we end up with `name`
++	 * advanced partway through the string. That's okay, though, as we
++	 * return immediately in those cases, without looking at `name` any
++	 * further.
++	 */
++	c = *(name++);
++	if (c == '.') {
++		/* .git */
++		if (((c = *(name++)) != 'g' && c != 'G') ||
++		    ((c = *(name++)) != 'i' && c != 'I') ||
++		    ((c = *(name++)) != 't' && c != 'T'))
+ 			return 0;
+-		}
++	} else if (c == 'g' || c == 'G') {
++		/* git ~1 */
++		if (((c = *(name++)) != 'i' && c != 'I') ||
++		    ((c = *(name++)) != 't' && c != 'T') ||
++		    *(name++) != '~' ||
++		    *(name++) != '1')
++			return 0;
++	} else
++		return 0;
++
++	for (;;) {
++		c = *(name++);
++		if (!c || c == '\\' || c == '/' || c == ':')
++			return 1;
++		if (c != '.' && c != ' ')
++			return 0;
++	}
+ }
+ 
+ static int is_ntfs_dot_generic(const char *name,
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0016-protect_ntfs-turn-on-NTFS-protection-by-default.diff b/debian/patches/0016-protect_ntfs-turn-on-NTFS-protection-by-default.diff
new file mode 100644
index 0000000..2018c43
--- /dev/null
+++ b/debian/patches/0016-protect_ntfs-turn-on-NTFS-protection-by-default.diff
@@ -0,0 +1,144 @@
+From ceda4f638bef0948bd3051eab2f7964275053126 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 9 Sep 2019 21:04:41 +0200
+Subject: protect_ntfs: turn on NTFS protection by default
+
+Back in the DOS days, in the FAT file system, file names always
+consisted of a base name of length 8 plus a file extension of length 3.
+Shorter file names were simply padded with spaces to the full 8.3
+format.
+
+Later, the FAT file system was taught to support _also_ longer names,
+with an 8.3 "short name" as primary file name. While at it, the same
+facility allowed formerly illegal file names, such as `.git` (empty base
+names were not allowed), which would have the "short name" `git~1`
+associated with it.
+
+For backwards-compatibility, NTFS supports alternative 8.3 short
+filenames, too, even if starting with Windows Vista, they are only
+generated on the system drive by default.
+
+We addressed the problem that the `.git/` directory can _also_ be
+accessed via `git~1/` (when short names are enabled) in 2b4c6efc821
+(read-cache: optionally disallow NTFS .git variants, 2014-12-16), i.e.
+since Git v1.9.5, by introducing the config setting `core.protectNTFS`
+and enabling it by default on Windows.
+
+In the meantime, Windows 10 introduced the "Windows Subsystem for Linux"
+(short: WSL), i.e. a way to run Linux applications/distributions in a
+thinly-isolated subsystem on Windows (giving rise to many a "2016 is the
+Year of Linux on the Desktop" jokes). WSL is getting increasingly
+popular, also due to the painless way Linux application can operate
+directly ("natively") on files on Windows' file system: the Windows
+drives are mounted automatically (e.g. `C:` as `/mnt/c/`).
+
+Taken together, this means that we now have to enable the safe-guards of
+Git v1.9.5 also in WSL: it is possible to access a `.git` directory
+inside `/mnt/c/` via the 8.3 name `git~1` (unless short name generation
+was disabled manually). Since regular Linux distributions run in WSL,
+this means we have to enable `core.protectNTFS` at least on Linux, too.
+
+To enable Services for Macintosh in Windows NT to store so-called
+resource forks, NTFS introduced "Alternate Data Streams". Essentially,
+these constitute additional metadata that are connected to (and copied
+with) their associated files, and they are accessed via pseudo file
+names of the form `filename:<stream-name>:<stream-type>`.
+
+In a recent patch, we extended `core.protectNTFS` to also protect
+against accesses via NTFS Alternate Data Streams, e.g. to prevent
+contents of the `.git/` directory to be "tracked" via yet another
+alternative file name.
+
+While it is not possible (at least by default) to access files via NTFS
+Alternate Data Streams from within WSL, the defaults on macOS when
+mounting network shares via SMB _do_ allow accessing files and
+directories in that way. Therefore, we need to enable `core.protectNTFS`
+on macOS by default, too, and really, on any Operating System that can
+mount network shares via SMB/CIFS.
+
+A couple of approaches were considered for fixing this:
+
+1. We could perform a dynamic NTFS check similar to the `core.symlinks`
+   check in `init`/`clone`: instead of trying to create a symbolic link
+   in the `.git/` directory, we could create a test file and try to
+   access `.git/config` via 8.3 name and/or Alternate Data Stream.
+
+2. We could simply "flip the switch" on `core.protectNTFS`, to make it
+   "on by default".
+
+The obvious downside of 1. is that it won't protect worktrees that were
+clone with a vulnerable Git version already. We considered patching code
+paths that check out files to check whether we're running on an NTFS
+system dynamically and persist the result in the repository-local config
+setting `core.protectNTFS`, but in the end decided that this solution
+would be too fragile, and too involved.
+
+The obvious downside of 2. is that everybody will have to "suffer" the
+performance penalty incurred from calling `is_ntfs_dotgit()` on every
+path, even in setups where.
+
+After the recent work to accelerate `is_ntfs_dotgit()` in most cases,
+it looks as if the time spent on validating ten million random
+file names increases only negligibly (less than 20ms, well within the
+standard deviation of ~50ms). Therefore the benefits outweigh the cost.
+
+Another downside of this is that paths that might have been acceptable
+previously now will be forbidden. Realistically, though, this is an
+improvement because public Git hosters already would reject any `git
+push` that contains such file names.
+
+Note: There might be a similar problem mounting HFS+ on Linux. However,
+this scenario has been considered unlikely and in light of the cost (in
+the aforementioned benchmark, `core.protectHFS = true` increased the
+time from ~440ms to ~610ms), it was decided _not_ to touch the default
+of `core.protectHFS`.
+
+This change addresses CVE-2019-1353.
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Helped-by: Garima Singh <garima.singh@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 9102f958ee5254b10c0be72672aa3305bf4f4704)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ config.mak.uname | 3 +--
+ environment.c    | 2 +-
+ 2 files changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/config.mak.uname b/config.mak.uname
+index 3ee7da0e23..85af9f9cf1 100644
+--- a/config.mak.uname
++++ b/config.mak.uname
+@@ -396,7 +396,6 @@ ifeq ($(uname_S),Windows)
+ 	EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
+ 	PTHREAD_LIBS =
+ 	lib =
+-	BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
+ ifndef DEBUG
+ 	BASIC_CFLAGS += -GL -Os -MD
+ 	BASIC_LDFLAGS += -LTCG
+@@ -529,7 +528,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
+ 	COMPAT_OBJS += compat/mingw.o compat/winansi.o \
+ 		compat/win32/pthread.o compat/win32/syslog.o \
+ 		compat/win32/dirent.o
+-	BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
++	BASIC_CFLAGS += -DWIN32
+ 	EXTLIBS += -lws2_32
+ 	GITLIBS += git.res
+ 	PTHREAD_LIBS =
+diff --git a/environment.c b/environment.c
+index 3465597707..c7b76d3bbb 100644
+--- a/environment.c
++++ b/environment.c
+@@ -80,7 +80,7 @@ enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
+ int protect_hfs = PROTECT_HFS_DEFAULT;
+ 
+ #ifndef PROTECT_NTFS_DEFAULT
+-#define PROTECT_NTFS_DEFAULT 0
++#define PROTECT_NTFS_DEFAULT 1
+ #endif
+ int protect_ntfs = PROTECT_NTFS_DEFAULT;
+ const char *core_fsmonitor;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0017-Disallow-dubiously-nested-submodule-git-directories.diff b/debian/patches/0017-Disallow-dubiously-nested-submodule-git-directories.diff
new file mode 100644
index 0000000..678f8d3
--- /dev/null
+++ b/debian/patches/0017-Disallow-dubiously-nested-submodule-git-directories.diff
@@ -0,0 +1,183 @@
+From 77346ef60d83c01922ba2c7ffbff6b22800215f2 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Tue, 1 Oct 2019 23:27:18 +0200
+Subject: Disallow dubiously-nested submodule git directories
+
+Currently it is technically possible to let a submodule's git
+directory point right into the git dir of a sibling submodule.
+
+Example: the git directories of two submodules with the names `hippo`
+and `hippo/hooks` would be `.git/modules/hippo/` and
+`.git/modules/hippo/hooks/`, respectively, but the latter is already
+intended to house the former's hooks.
+
+In most cases, this is just confusing, but there is also a (quite
+contrived) attack vector where Git can be fooled into mistaking remote
+content for file contents it wrote itself during a recursive clone.
+
+Let's plug this bug.
+
+To do so, we introduce the new function `validate_submodule_git_dir()`
+which simply verifies that no git dir exists for any leading directories
+of the submodule name (if there are any).
+
+Note: this patch specifically continues to allow sibling modules names
+of the form `core/lib`, `core/doc`, etc, as long as `core` is not a
+submodule name.
+
+This fixes CVE-2019-1387.
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit a8dee3ca610f5a1d403634492136c887f83b59d2)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ builtin/submodule--helper.c |  4 +++
+ submodule.c                 | 49 +++++++++++++++++++++++++++++++++++--
+ submodule.h                 |  5 ++++
+ t/t7415-submodule-names.sh  | 23 +++++++++++++++++
+ 4 files changed, 79 insertions(+), 2 deletions(-)
+
+diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
+index e103abbcbc..b93d624a85 100644
+--- a/builtin/submodule--helper.c
++++ b/builtin/submodule--helper.c
+@@ -1411,6 +1411,10 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 	} else
+ 		path = xstrdup(path);
+ 
++	if (validate_submodule_git_dir(sm_gitdir, name) < 0)
++		die(_("refusing to create/use '%s' in another submodule's "
++			"git dir"), sm_gitdir);
++
+ 	if (!file_exists(sm_gitdir)) {
+ 		if (safe_create_leading_directories_const(sm_gitdir) < 0)
+ 			die(_("could not create directory '%s'"), sm_gitdir);
+diff --git a/submodule.c b/submodule.c
+index 6415cc5580..37d29bb252 100644
+--- a/submodule.c
++++ b/submodule.c
+@@ -1732,6 +1732,47 @@ int submodule_move_head(const char *path,
+ 	return ret;
+ }
+ 
++int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
++{
++	size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
++	char *p;
++	int ret = 0;
++
++	if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
++	    strcmp(p, submodule_name))
++		BUG("submodule name '%s' not a suffix of git dir '%s'",
++		    submodule_name, git_dir);
++
++	/*
++	 * We prevent the contents of sibling submodules' git directories to
++	 * clash.
++	 *
++	 * Example: having a submodule named `hippo` and another one named
++	 * `hippo/hooks` would result in the git directories
++	 * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
++	 * but the latter directory is already designated to contain the hooks
++	 * of the former.
++	 */
++	for (; *p; p++) {
++		if (is_dir_sep(*p)) {
++			char c = *p;
++
++			*p = '\0';
++			if (is_git_directory(git_dir))
++				ret = -1;
++			*p = c;
++
++			if (ret < 0)
++				return error(_("submodule git dir '%s' is "
++					       "inside git dir '%.*s'"),
++					     git_dir,
++					     (int)(p - git_dir), git_dir);
++		}
++	}
++
++	return 0;
++}
++
+ /*
+  * Embeds a single submodules git directory into the superprojects git dir,
+  * non recursively.
+@@ -1740,7 +1781,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
+ 						      const char *path)
+ {
+ 	char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
+-	const char *new_git_dir;
++	char *new_git_dir;
+ 	const struct submodule *sub;
+ 
+ 	if (submodule_uses_worktrees(path))
+@@ -1758,10 +1799,14 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
+ 	if (!sub)
+ 		die(_("could not lookup name for submodule '%s'"), path);
+ 
+-	new_git_dir = git_path("modules/%s", sub->name);
++	new_git_dir = git_pathdup("modules/%s", sub->name);
++	if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
++		die(_("refusing to move '%s' into an existing git dir"),
++		    real_old_git_dir);
+ 	if (safe_create_leading_directories_const(new_git_dir) < 0)
+ 		die(_("could not create directory '%s'"), new_git_dir);
+ 	real_new_git_dir = real_pathdup(new_git_dir, 1);
++	free(new_git_dir);
+ 
+ 	fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
+ 		get_super_prefix_or_empty(), path,
+diff --git a/submodule.h b/submodule.h
+index a680214c01..ac206dc182 100644
+--- a/submodule.h
++++ b/submodule.h
+@@ -124,6 +124,11 @@ int push_unpushed_submodules(struct repository *r,
+  */
+ int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
+ 
++/*
++ * Make sure that no submodule's git dir is nested in a sibling submodule's.
++ */
++int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
++
+ #define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
+ #define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
+ int submodule_move_head(const char *path,
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index 9b69aed48e..71391fe161 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -224,4 +224,27 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
+ 	! grep gitdir squatting-clone/d/a/git~2
+ '
+ 
++test_expect_success 'git dirs of sibling submodules must not be nested' '
++	git init nested &&
++	test_commit -C nested nested &&
++	(
++		cd nested &&
++		cat >.gitmodules <<-EOF &&
++		[submodule "hippo"]
++			url = .
++			path = thing1
++		[submodule "hippo/hooks"]
++			url = .
++			path = thing2
++		EOF
++		git clone . thing1 &&
++		git clone . thing2 &&
++		git add .gitmodules thing1 thing2 &&
++		test_tick &&
++		git commit -m nested
++	) &&
++	test_must_fail git clone --recurse-submodules nested clone 2>err &&
++	test_i18ngrep "is inside git dir" err
++'
++
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0018-mingw-fix-quoting-of-arguments.diff b/debian/patches/0018-mingw-fix-quoting-of-arguments.diff
new file mode 100644
index 0000000..c77baaa
--- /dev/null
+++ b/debian/patches/0018-mingw-fix-quoting-of-arguments.diff
@@ -0,0 +1,100 @@
+From e7552c07af3edb76594f4ad9ec45559d6cf47612 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Fri, 13 Sep 2019 16:32:43 +0200
+Subject: mingw: fix quoting of arguments
+
+We need to be careful to follow proper quoting rules. For example, if an
+argument contains spaces, we have to quote them. Double-quotes need to
+be escaped. Backslashes need to be escaped, but only if they are
+followed by a double-quote character.
+
+We need to be _extra_ careful to consider the case where an argument
+ends in a backslash _and_ needs to be quoted: in this case, we append a
+double-quote character, i.e. the backslash now has to be escaped!
+
+The current code, however, fails to recognize that, and therefore can
+turn an argument that ends in a single backslash into a quoted argument
+that now ends in an escaped double-quote character. This allows
+subsequent command-line parameters to be split and part of them being
+mistaken for command-line options, e.g. through a maliciously-crafted
+submodule URL during a recursive clone.
+
+Technically, we would not need to quote _all_ arguments which end in a
+backslash _unless_ the argument needs to be quoted anyway. For example,
+`test\` would not need to be quoted, while `test \` would need to be.
+
+To keep the code simple, however, and therefore easier to reason about
+and ensure its correctness, we now _always_ quote an argument that ends
+in a backslash.
+
+This addresses CVE-2019-1350.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 6d8684161ee9c03bed5cb69ae76dfdddb85a0003)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ compat/mingw.c                |  9 ++++++---
+ t/t7416-submodule-dash-url.sh | 14 ++++++++++++++
+ 2 files changed, 20 insertions(+), 3 deletions(-)
+
+diff --git a/compat/mingw.c b/compat/mingw.c
+index 34b3880b29..94b0746f2f 100644
+--- a/compat/mingw.c
++++ b/compat/mingw.c
+@@ -1051,7 +1051,7 @@ static const char *quote_arg(const char *arg)
+ 				p++;
+ 				len++;
+ 			}
+-			if (*p == '"')
++			if (*p == '"' || !*p)
+ 				n += count*2 + 1;
+ 			continue;
+ 		}
+@@ -1073,16 +1073,19 @@ static const char *quote_arg(const char *arg)
+ 				count++;
+ 				*d++ = *arg++;
+ 			}
+-			if (*arg == '"') {
++			if (*arg == '"' || !*arg) {
+ 				while (count-- > 0)
+ 					*d++ = '\\';
++				/* don't escape the surrounding end quote */
++				if (!*arg)
++					break;
+ 				*d++ = '\\';
+ 			}
+ 		}
+ 		*d++ = *arg++;
+ 	}
+ 	*d++ = '"';
+-	*d++ = 0;
++	*d++ = '\0';
+ 	return q;
+ }
+ 
+diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
+index 1cd2c1c1ea..5ba041f537 100755
+--- a/t/t7416-submodule-dash-url.sh
++++ b/t/t7416-submodule-dash-url.sh
+@@ -46,4 +46,18 @@ test_expect_success 'fsck rejects unprotected dash' '
+ 	grep gitmodulesUrl err
+ '
+ 
++test_expect_success 'trailing backslash is handled correctly' '
++	git init testmodule &&
++	test_commit -C testmodule c &&
++	git submodule add ./testmodule &&
++	: ensure that the name ends in a double backslash &&
++	sed -e "s|\\(submodule \"testmodule\\)\"|\\1\\\\\\\\\"|" \
++		-e "s|url = .*|url = \" --should-not-be-an-option\"|" \
++		<.gitmodules >.new &&
++	mv .new .gitmodules &&
++	git commit -am "Add testmodule" &&
++	test_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&
++	test_i18ngrep ! "unknown option" err
++'
++
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0019-tests-add-a-helper-to-stress-test-argument-quoting.diff b/debian/patches/0019-tests-add-a-helper-to-stress-test-argument-quoting.diff
new file mode 100644
index 0000000..76e5a31
--- /dev/null
+++ b/debian/patches/0019-tests-add-a-helper-to-stress-test-argument-quoting.diff
@@ -0,0 +1,165 @@
+From c8c38a021ac67f242bf486537b604c1647e357df Mon Sep 17 00:00:00 2001
+From: Garima Singh <garima.singh@microsoft.com>
+Date: Wed, 18 Sep 2019 16:03:59 -0400
+Subject: tests: add a helper to stress test argument quoting
+
+On Windows, we have to do all the command-line argument quoting
+ourselves. Worse: we have to have two versions of said quoting, one for
+MSYS2 programs (which have their own dequoting rules) and the rest.
+
+We care mostly about the rest, and to make sure that that works, let's
+have a stress test that comes up with all kinds of awkward arguments,
+verifying that a spawned sub-process receives those unharmed.
+
+Signed-off-by: Garima Singh <garima.singh@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit ad1559252945179e28fba7d693494051352810c5)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/helper/test-run-command.c | 118 +++++++++++++++++++++++++++++++++++-
+ 1 file changed, 116 insertions(+), 2 deletions(-)
+
+diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
+index 2cc93bb69c..bdab5d362a 100644
+--- a/t/helper/test-run-command.c
++++ b/t/helper/test-run-command.c
+@@ -13,8 +13,8 @@
+ #include "run-command.h"
+ #include "argv-array.h"
+ #include "strbuf.h"
+-#include <string.h>
+-#include <errno.h>
++#include "gettext.h"
++#include "parse-options.h"
+ 
+ static int number_callbacks;
+ static int parallel_next(struct child_process *cp,
+@@ -50,11 +50,125 @@ static int task_finished(int result,
+ 	return 1;
+ }
+ 
++static uint64_t my_random_next = 1234;
++
++static uint64_t my_random(void)
++{
++	uint64_t res = my_random_next;
++	my_random_next = my_random_next * 1103515245 + 12345;
++	return res;
++}
++
++static int quote_stress_test(int argc, const char **argv)
++{
++	/*
++	 * We are running a quote-stress test.
++	 * spawn a subprocess that runs quote-stress with a
++	 * special option that echoes back the arguments that
++	 * were passed in.
++	 */
++	char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
++	int i, j, k, trials = 100;
++	struct strbuf out = STRBUF_INIT;
++	struct argv_array args = ARGV_ARRAY_INIT;
++	struct option options[] = {
++		OPT_INTEGER('n', "trials", &trials, "Number of trials"),
++		OPT_END()
++	};
++	const char * const usage[] = {
++		"test-tool run-command quote-stress-test <options>",
++		NULL
++	};
++
++	argc = parse_options(argc, argv, NULL, options, usage, 0);
++
++	for (i = 0; i < trials; i++) {
++		struct child_process cp = CHILD_PROCESS_INIT;
++		size_t arg_count = 1 + (my_random() % 5), arg_offset;
++		int ret = 0;
++
++		argv_array_clear(&args);
++		argv_array_pushl(&args, "test-tool", "run-command",
++				 "quote-echo", NULL);
++		arg_offset = args.argc;
++		for (j = 0; j < arg_count; j++) {
++			char buf[20];
++			size_t min_len = 1;
++			size_t arg_len = min_len +
++				(my_random() % (ARRAY_SIZE(buf) - min_len));
++
++			for (k = 0; k < arg_len; k++)
++				buf[k] = special[my_random() %
++					ARRAY_SIZE(special)];
++			buf[arg_len] = '\0';
++
++			argv_array_push(&args, buf);
++		}
++
++		cp.argv = args.argv;
++		strbuf_reset(&out);
++		if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
++			return error("Failed to spawn child process");
++
++		for (j = 0, k = 0; j < arg_count; j++) {
++			const char *arg = args.argv[j + arg_offset];
++
++			if (strcmp(arg, out.buf + k))
++				ret = error("incorrectly quoted arg: '%s', "
++					    "echoed back as '%s'",
++					     arg, out.buf + k);
++			k += strlen(out.buf + k) + 1;
++		}
++
++		if (k != out.len)
++			ret = error("got %d bytes, but consumed only %d",
++				     (int)out.len, (int)k);
++
++		if (ret) {
++			fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
++			for (j = 0; j < arg_count; j++)
++				fprintf(stderr, "arg #%d: '%s'\n",
++					(int)j, args.argv[j + arg_offset]);
++
++			strbuf_release(&out);
++			argv_array_clear(&args);
++
++			return ret;
++		}
++
++		if (i && (i % 100) == 0)
++			fprintf(stderr, "Trials completed: %d\n", (int)i);
++	}
++
++	strbuf_release(&out);
++	argv_array_clear(&args);
++
++	return 0;
++}
++
++static int quote_echo(int argc, const char **argv)
++{
++	while (argc > 1) {
++		fwrite(argv[1], strlen(argv[1]), 1, stdout);
++		fputc('\0', stdout);
++		argv++;
++		argc--;
++	}
++
++	return 0;
++}
++
+ int cmd__run_command(int argc, const char **argv)
+ {
+ 	struct child_process proc = CHILD_PROCESS_INIT;
+ 	int jobs;
+ 
++	if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
++		return !!quote_stress_test(argc - 1, argv + 1);
++
++	if (argc >= 2 && !strcmp(argv[1], "quote-echo"))
++		return !!quote_echo(argc - 1, argv + 1);
++
+ 	if (argc < 3)
+ 		return 1;
+ 	while (!strcmp(argv[1], "env")) {
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0020-quote-stress-test-accept-arguments-to-test-via-the-co.diff b/debian/patches/0020-quote-stress-test-accept-arguments-to-test-via-the-co.diff
new file mode 100644
index 0000000..d2627ef
--- /dev/null
+++ b/debian/patches/0020-quote-stress-test-accept-arguments-to-test-via-the-co.diff
@@ -0,0 +1,72 @@
+From 24687a7776f46f71f784704248cff970baf4da9c Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Fri, 20 Sep 2019 19:09:39 +0200
+Subject: quote-stress-test: accept arguments to test via the command-line
+
+When the stress test reported a problem with quoting certain arguments,
+it is helpful to have a facility to play with those arguments in order
+to find out whether variations of those arguments are affected, too.
+
+Let's allow `test-run-command quote-stress-test -- <args>` to be used
+for that purpose.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 55953c77c0bfcb727ffd7e293e4661b7a24b791b)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/helper/test-run-command.c | 31 ++++++++++++++++++++-----------
+ 1 file changed, 20 insertions(+), 11 deletions(-)
+
+diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
+index bdab5d362a..d310e79f98 100644
+--- a/t/helper/test-run-command.c
++++ b/t/helper/test-run-command.c
+@@ -84,25 +84,34 @@ static int quote_stress_test(int argc, const char **argv)
+ 
+ 	for (i = 0; i < trials; i++) {
+ 		struct child_process cp = CHILD_PROCESS_INIT;
+-		size_t arg_count = 1 + (my_random() % 5), arg_offset;
++		size_t arg_count, arg_offset;
+ 		int ret = 0;
+ 
+ 		argv_array_clear(&args);
+ 		argv_array_pushl(&args, "test-tool", "run-command",
+ 				 "quote-echo", NULL);
+ 		arg_offset = args.argc;
+-		for (j = 0; j < arg_count; j++) {
+-			char buf[20];
+-			size_t min_len = 1;
+-			size_t arg_len = min_len +
+-				(my_random() % (ARRAY_SIZE(buf) - min_len));
+ 
+-			for (k = 0; k < arg_len; k++)
+-				buf[k] = special[my_random() %
+-					ARRAY_SIZE(special)];
+-			buf[arg_len] = '\0';
++		if (argc > 0) {
++			trials = 1;
++			arg_count = argc;
++			for (j = 0; j < arg_count; j++)
++				argv_array_push(&args, argv[j]);
++		} else {
++			arg_count = 1 + (my_random() % 5);
++			for (j = 0; j < arg_count; j++) {
++				char buf[20];
++				size_t min_len = 1;
++				size_t arg_len = min_len +
++					(my_random() % (ARRAY_SIZE(buf) - min_len));
+ 
+-			argv_array_push(&args, buf);
++				for (k = 0; k < arg_len; k++)
++					buf[k] = special[my_random() %
++						ARRAY_SIZE(special)];
++				buf[arg_len] = '\0';
++
++				argv_array_push(&args, buf);
++			}
+ 		}
+ 
+ 		cp.argv = args.argv;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0021-quote-stress-test-allow-skipping-some-trials.diff b/debian/patches/0021-quote-stress-test-allow-skipping-some-trials.diff
new file mode 100644
index 0000000..a7fa4ce
--- /dev/null
+++ b/debian/patches/0021-quote-stress-test-allow-skipping-some-trials.diff
@@ -0,0 +1,46 @@
+From 2f76b19a118b92ea502766d8620f1c0abba387bd Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Thu, 19 Sep 2019 23:46:31 +0200
+Subject: quote-stress-test: allow skipping some trials
+
+When the, say, 93rd trial run fails, it is a good idea to have a way to
+skip the first 92 trials and dig directly into the 93rd in a debugger.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 7530a6287e20a74b9fe6d4ca3a66df0f0f5cc52c)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/helper/test-run-command.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
+index d310e79f98..6da11d3a3c 100644
+--- a/t/helper/test-run-command.c
++++ b/t/helper/test-run-command.c
+@@ -68,11 +68,12 @@ static int quote_stress_test(int argc, const char **argv)
+ 	 * were passed in.
+ 	 */
+ 	char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
+-	int i, j, k, trials = 100;
++	int i, j, k, trials = 100, skip = 0;
+ 	struct strbuf out = STRBUF_INIT;
+ 	struct argv_array args = ARGV_ARRAY_INIT;
+ 	struct option options[] = {
+ 		OPT_INTEGER('n', "trials", &trials, "Number of trials"),
++		OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
+ 		OPT_END()
+ 	};
+ 	const char * const usage[] = {
+@@ -114,6 +115,9 @@ static int quote_stress_test(int argc, const char **argv)
+ 			}
+ 		}
+ 
++		if (i < skip)
++			continue;
++
+ 		cp.argv = args.argv;
+ 		strbuf_reset(&out);
+ 		if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0022-quote-stress-test-offer-to-test-quoting-arguments-for.diff b/debian/patches/0022-quote-stress-test-offer-to-test-quoting-arguments-for.diff
new file mode 100644
index 0000000..af0163c
--- /dev/null
+++ b/debian/patches/0022-quote-stress-test-offer-to-test-quoting-arguments-for.diff
@@ -0,0 +1,64 @@
+From 29077d3748c8e61ad54ac90ee95f3d3691f7606e Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Fri, 20 Sep 2019 00:12:37 +0200
+Subject: quote-stress-test: offer to test quoting arguments for MSYS2 sh
+
+It is unfortunate that we need to quote arguments differently on
+Windows, depending whether we build a command-line for MSYS2's `sh` or
+for other Windows executables.
+
+We already have a test helper to verify the latter, with this patch we
+can also verify the former.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 379e51d1ae668a1f26d50eb59b3f8befc1eb8883)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/helper/test-run-command.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
+index 6da11d3a3c..8579b1f7d1 100644
+--- a/t/helper/test-run-command.c
++++ b/t/helper/test-run-command.c
+@@ -68,12 +68,13 @@ static int quote_stress_test(int argc, const char **argv)
+ 	 * were passed in.
+ 	 */
+ 	char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
+-	int i, j, k, trials = 100, skip = 0;
++	int i, j, k, trials = 100, skip = 0, msys2 = 0;
+ 	struct strbuf out = STRBUF_INIT;
+ 	struct argv_array args = ARGV_ARRAY_INIT;
+ 	struct option options[] = {
+ 		OPT_INTEGER('n', "trials", &trials, "Number of trials"),
+ 		OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
++		OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"),
+ 		OPT_END()
+ 	};
+ 	const char * const usage[] = {
+@@ -83,14 +84,20 @@ static int quote_stress_test(int argc, const char **argv)
+ 
+ 	argc = parse_options(argc, argv, NULL, options, usage, 0);
+ 
++	setenv("MSYS_NO_PATHCONV", "1", 0);
++
+ 	for (i = 0; i < trials; i++) {
+ 		struct child_process cp = CHILD_PROCESS_INIT;
+ 		size_t arg_count, arg_offset;
+ 		int ret = 0;
+ 
+ 		argv_array_clear(&args);
+-		argv_array_pushl(&args, "test-tool", "run-command",
+-				 "quote-echo", NULL);
++		if (msys2)
++			argv_array_pushl(&args, "sh", "-c",
++					 "printf %s\\\\0 \"$@\"", "skip", NULL);
++		else
++			argv_array_pushl(&args, "test-tool", "run-command",
++					 "quote-echo", NULL);
+ 		arg_offset = args.argc;
+ 
+ 		if (argc > 0) {
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0023-t6130-t9350-prepare-for-stringent-Win32-path-validati.diff b/debian/patches/0023-t6130-t9350-prepare-for-stringent-Win32-path-validati.diff
new file mode 100644
index 0000000..3d1b9dc
--- /dev/null
+++ b/debian/patches/0023-t6130-t9350-prepare-for-stringent-Win32-path-validati.diff
@@ -0,0 +1,52 @@
+From 22309546a40d318d6d48f0429b2340a05dea3d08 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 9 Sep 2019 15:43:35 +0200
+Subject: t6130/t9350: prepare for stringent Win32 path validation
+
+On Windows, file names cannot contain asterisks nor newline characters.
+In an upcoming commit, we will make this limitation explicit,
+disallowing even the creation of commits that introduce such file names.
+
+However, in the test scripts touched by this patch, we _know_ that those
+paths won't be checked out, so we _want_ to allow such file names.
+
+Happily, the stringent path validation will be guarded via the
+`core.protectNTFS` flag, so all we need to do is to force that flag off
+temporarily.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 35edce205615c553fdc49bcf10b0c91f061c56c9)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t6130-pathspec-noglob.sh | 1 +
+ t/t9350-fast-export.sh     | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
+index 37760233a5..ba7902c9cd 100755
+--- a/t/t6130-pathspec-noglob.sh
++++ b/t/t6130-pathspec-noglob.sh
+@@ -10,6 +10,7 @@ test_expect_success 'create commits with glob characters' '
+ 	# the name "f*" in the worktree, because it is not allowed
+ 	# on Windows (the tests below do not depend on the presence
+ 	# of the file in the worktree)
++	git config core.protectNTFS false &&
+ 	git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:foo)" "f*" &&
+ 	test_tick &&
+ 	git commit -m star &&
+diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
+index 34124be778..ef1e01805b 100755
+--- a/t/t9350-fast-export.sh
++++ b/t/t9350-fast-export.sh
+@@ -421,7 +421,7 @@ test_expect_success 'fast-export quotes pathnames' '
+ 	test_config -C crazy-paths core.protectNTFS false &&
+ 	(cd crazy-paths &&
+ 	 blob=$(echo foo | git hash-object -w --stdin) &&
+-	 git update-index --add \
++	 git -c core.protectNTFS=false update-index --add \
+ 		--cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
+ 		--cacheinfo 100644 $blob "path with \"quote\"" \
+ 		--cacheinfo 100644 $blob "path with \\backslash" \
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0024-unpack-trees-let-merged_entry-pass-through-do_add_ent.diff b/debian/patches/0024-unpack-trees-let-merged_entry-pass-through-do_add_ent.diff
new file mode 100644
index 0000000..0296fca
--- /dev/null
+++ b/debian/patches/0024-unpack-trees-let-merged_entry-pass-through-do_add_ent.diff
@@ -0,0 +1,43 @@
+From 64e76745d7a6c453c9d868d2ff6c2e731d33fda7 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 9 Sep 2019 13:56:15 +0200
+Subject: unpack-trees: let merged_entry() pass through do_add_entry()'s errors
+
+A `git clone` will end with exit code 0 when `merged_entry()` returns a
+positive value during a call of `unpack_trees()` to `traverse_trees()`.
+The reason is that `unpack_trees()` will interpret a positive value not
+to be an error.
+
+The problem is, however, that `add_index_entry()` (which is called by
+`merged_entry()` can report an error, and we really should fail the
+entire clone in such a case.
+
+Let's fix this problem, in preparation for a Windows-specific patch
+disallowing `mkdir()` with directory names that contain a trailing space
+(which is illegal on NTFS): we want `git clone` to abort when a path
+cannot be checked out due to that condition.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit cc756edda63769cf6d7acc99e6ad3a9cbb5dc3ec)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ unpack-trees.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/unpack-trees.c b/unpack-trees.c
+index 7570df481b..545d5668fd 100644
+--- a/unpack-trees.c
++++ b/unpack-trees.c
+@@ -2073,7 +2073,8 @@ static int merged_entry(const struct cache_entry *ce,
+ 		invalidate_ce_path(old, o);
+ 	}
+ 
+-	do_add_entry(o, merge, update, CE_STAGEMASK);
++	if (do_add_entry(o, merge, update, CE_STAGEMASK) < 0)
++		return -1;
+ 	return 1;
+ }
+ 
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0025-mingw-refuse-to-access-paths-with-trailing-spaces-or-.diff b/debian/patches/0025-mingw-refuse-to-access-paths-with-trailing-spaces-or-.diff
new file mode 100644
index 0000000..10079b7
--- /dev/null
+++ b/debian/patches/0025-mingw-refuse-to-access-paths-with-trailing-spaces-or-.diff
@@ -0,0 +1,317 @@
+From d47ef968cb7f7d752ef8bda65f73e16b163f96a4 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Thu, 5 Sep 2019 13:27:53 +0200
+Subject: mingw: refuse to access paths with trailing spaces or periods
+
+When creating a directory on Windows whose path ends in a space or a
+period (or chains thereof), the Win32 API "helpfully" trims those. For
+example, `mkdir("abc ");` will return success, but actually create a
+directory called `abc` instead.
+
+This stems back to the DOS days, when all file names had exactly 8
+characters plus exactly 3 characters for the file extension, and the
+only way to have shorter names was by padding with spaces.
+
+Sadly, this "helpful" behavior is a bit inconsistent: after a successful
+`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
+the directory `abc ` does not actually exist).
+
+Even if it would work, we now have a serious problem because a Git
+repository could contain directories `abc` and `abc `, and on Windows,
+they would be "merged" unintentionally.
+
+As these paths are illegal on Windows, anyway, let's disallow any
+accesses to such paths on that Operating System.
+
+For practical reasons, this behavior is still guarded by the
+config setting `core.protectNTFS`: it is possible (and at least two
+regression tests make use of it) to create commits without involving the
+worktree. In such a scenario, it is of course possible -- even on
+Windows -- to create such file names.
+
+Among other consequences, this patch disallows submodules' paths to end
+in spaces on Windows (which would formerly have confused Git enough to
+try to write into incorrect paths, anyway).
+
+While this patch does not fix a vulnerability on its own, it prevents an
+attack vector that was exploited in demonstrations of a number of
+recently-fixed security bugs.
+
+The regression test added to `t/t7417-submodule-path-url.sh` reflects
+that attack vector.
+
+Note that we have to adjust the test case "prevent git~1 squatting on
+Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
+It tries to clone two submodules whose names differ only in a trailing
+period character, and as a consequence their git directories differ in
+the same way. Previously, when Git tried to clone the second submodule,
+it thought that the git directory already existed (because on Windows,
+when you create a directory with the name `b.` it actually creates `b`),
+but with this patch, the first submodule's clone will fail because of
+the illegal name of the git directory. Therefore, when cloning the
+second submodule, Git will take a different code path: a fresh clone
+(without an existing git directory). Both code paths fail to clone the
+second submodule, both because the the corresponding worktree directory
+exists and is not empty, but the error messages are worded differently.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit d2c84dad1c88f40906799bc879f70b965efd8ba6)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ compat/mingw.c                | 57 ++++++++++++++++++++++++++++++++++-
+ compat/mingw.h                | 11 +++++++
+ git-compat-util.h             |  4 +++
+ read-cache.c                  |  3 ++
+ t/helper/test-path-utils.c    | 17 +++++++++++
+ t/t0060-path-utils.sh         | 14 +++++++++
+ t/t7415-submodule-names.sh    |  2 +-
+ t/t7417-submodule-path-url.sh | 17 +++++++++++
+ 8 files changed, 123 insertions(+), 2 deletions(-)
+
+diff --git a/compat/mingw.c b/compat/mingw.c
+index 94b0746f2f..386aa94acb 100644
+--- a/compat/mingw.c
++++ b/compat/mingw.c
+@@ -389,6 +389,12 @@ int mingw_mkdir(const char *path, int mode)
+ {
+ 	int ret;
+ 	wchar_t wpath[MAX_PATH];
++
++	if (!is_valid_win32_path(path)) {
++		errno = EINVAL;
++		return -1;
++	}
++
+ 	if (xutftowcs_path(wpath, path) < 0)
+ 		return -1;
+ 	ret = _wmkdir(wpath);
+@@ -462,7 +468,7 @@ int mingw_open (const char *filename, int oflags, ...)
+ 	typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
+ 	va_list args;
+ 	unsigned mode;
+-	int fd;
++	int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
+ 	wchar_t wfilename[MAX_PATH];
+ 	open_fn_t open_fn;
+ 
+@@ -470,6 +476,11 @@ int mingw_open (const char *filename, int oflags, ...)
+ 	mode = va_arg(args, int);
+ 	va_end(args);
+ 
++	if (!is_valid_win32_path(filename)) {
++		errno = create ? EINVAL : ENOENT;
++		return -1;
++	}
++
+ 	if (filename && !strcmp(filename, "/dev/null"))
+ 		filename = "nul";
+ 
+@@ -536,6 +547,11 @@ FILE *mingw_fopen (const char *filename, const char *otype)
+ 	int hide = needs_hiding(filename);
+ 	FILE *file;
+ 	wchar_t wfilename[MAX_PATH], wotype[4];
++	if (!is_valid_win32_path(filename)) {
++		int create = otype && strchr(otype, 'w');
++		errno = create ? EINVAL : ENOENT;
++		return NULL;
++	}
+ 	if (filename && !strcmp(filename, "/dev/null"))
+ 		filename = "nul";
+ 	if (xutftowcs_path(wfilename, filename) < 0 ||
+@@ -558,6 +574,11 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
+ 	int hide = needs_hiding(filename);
+ 	FILE *file;
+ 	wchar_t wfilename[MAX_PATH], wotype[4];
++	if (!is_valid_win32_path(filename)) {
++		int create = otype && strchr(otype, 'w');
++		errno = create ? EINVAL : ENOENT;
++		return NULL;
++	}
+ 	if (filename && !strcmp(filename, "/dev/null"))
+ 		filename = "nul";
+ 	if (xutftowcs_path(wfilename, filename) < 0 ||
+@@ -2419,6 +2440,40 @@ static void setup_windows_environment(void)
+ 		setenv("TERM", "cygwin", 1);
+ }
+ 
++int is_valid_win32_path(const char *path)
++{
++	int preceding_space_or_period = 0, i = 0, periods = 0;
++
++	if (!protect_ntfs)
++		return 1;
++
++	for (;;) {
++		char c = *(path++);
++		switch (c) {
++		case '\0':
++		case '/': case '\\':
++			/* cannot end in ` ` or `.`, except for `.` and `..` */
++			if (preceding_space_or_period &&
++			    (i != periods || periods > 2))
++				return 0;
++			if (!c)
++				return 1;
++
++			i = periods = preceding_space_or_period = 0;
++			continue;
++		case '.':
++			periods++;
++			/* fallthru */
++		case ' ':
++			preceding_space_or_period = 1;
++			i++;
++			continue;
++		}
++		preceding_space_or_period = 0;
++		i++;
++	}
++}
++
+ /*
+  * Disable MSVCRT command line wildcard expansion (__getmainargs called from
+  * mingw startup code, see init.c in mingw runtime).
+diff --git a/compat/mingw.h b/compat/mingw.h
+index 8c24ddaa3e..1d7e9d7c9d 100644
+--- a/compat/mingw.h
++++ b/compat/mingw.h
+@@ -479,6 +479,17 @@ extern char *mingw_query_user_email(void);
+ #include <inttypes.h>
+ #endif
+ 
++/**
++ * Verifies that the given path is a valid one on Windows.
++ *
++ * In particular, path segments are disallowed which end in a period or a
++ * space (except the special directories `.` and `..`).
++ *
++ * Returns 1 upon success, otherwise 0.
++ */
++int is_valid_win32_path(const char *path);
++#define is_valid_path(path) is_valid_win32_path(path)
++
+ /**
+  * Converts UTF-8 encoded string to UTF-16LE.
+  *
+diff --git a/git-compat-util.h b/git-compat-util.h
+index 09b0102cae..d17360e897 100644
+--- a/git-compat-util.h
++++ b/git-compat-util.h
+@@ -385,6 +385,10 @@ static inline int git_offset_1st_component(const char *path)
+ #define offset_1st_component git_offset_1st_component
+ #endif
+ 
++#ifndef is_valid_path
++#define is_valid_path(path) 1
++#endif
++
+ #ifndef find_last_dir_sep
+ static inline char *git_find_last_dir_sep(const char *path)
+ {
+diff --git a/read-cache.c b/read-cache.c
+index 1a4e184478..1d82dbdd65 100644
+--- a/read-cache.c
++++ b/read-cache.c
+@@ -955,6 +955,9 @@ int verify_path(const char *path, unsigned mode)
+ 	if (has_dos_drive_prefix(path))
+ 		return 0;
+ 
++	if (!is_valid_path(path))
++		return 0;
++
+ 	goto inside;
+ 	for (;;) {
+ 		if (!c)
+diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
+index d9411032d2..e737a941d3 100644
+--- a/t/helper/test-path-utils.c
++++ b/t/helper/test-path-utils.c
+@@ -387,6 +387,23 @@ int cmd__path_utils(int argc, const char **argv)
+ 	if (argc > 1 && !strcmp(argv[1], "protect_ntfs_hfs"))
+ 		return !!protect_ntfs_hfs_benchmark(argc - 1, argv + 1);
+ 
++	if (argc > 1 && !strcmp(argv[1], "is_valid_path")) {
++		int res = 0, expect = 1, i;
++
++		for (i = 2; i < argc; i++)
++			if (!strcmp("--not", argv[i]))
++				expect = 0;
++			else if (expect != is_valid_path(argv[i]))
++				res = error("'%s' is%s a valid path",
++					    argv[i], expect ? " not" : "");
++			else
++				fprintf(stderr,
++					"'%s' is%s a valid path\n",
++					argv[i], expect ? "" : " not");
++
++		return !!res;
++	}
++
+ 	fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+ 		argv[1] ? argv[1] : "(there was none)");
+ 	return 1;
+diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
+index 85cccc655a..dc245c7dfe 100755
+--- a/t/t0060-path-utils.sh
++++ b/t/t0060-path-utils.sh
+@@ -455,4 +455,18 @@ test_expect_success 'match .gitmodules' '
+ 		.gitmodules,:\$DATA
+ '
+ 
++test_expect_success MINGW 'is_valid_path() on Windows' '
++       test-tool path-utils is_valid_path \
++		win32 \
++		"win32 x" \
++		../hello.txt \
++		\
++		--not \
++		"win32 "  \
++		"win32 /x "  \
++		"win32."  \
++		"win32 . ." \
++		.../hello.txt
++'
++
+ test_done
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index 71391fe161..33a9126ee0 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -220,7 +220,7 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
+ 	) &&
+ 	test_must_fail git -c core.protectNTFS=false \
+ 		clone --recurse-submodules squatting squatting-clone 2>err &&
+-	test_i18ngrep "directory not empty" err &&
++	test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
+ 	! grep gitdir squatting-clone/d/a/git~2
+ '
+ 
+diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
+index 756af8c4d6..f7e7e94d7b 100755
+--- a/t/t7417-submodule-path-url.sh
++++ b/t/t7417-submodule-path-url.sh
+@@ -25,4 +25,21 @@ test_expect_success 'fsck rejects unprotected dash' '
+ 	grep gitmodulesPath err
+ '
+ 
++test_expect_success MINGW 'submodule paths disallows trailing spaces' '
++	git init super &&
++	test_must_fail git -C super submodule add ../upstream "sub " &&
++
++	: add "sub", then rename "sub" to "sub ", the hard way &&
++	git -C super submodule add ../upstream sub &&
++	tree=$(git -C super write-tree) &&
++	git -C super ls-tree $tree >tree &&
++	sed "s/sub/sub /" <tree >tree.new &&
++	tree=$(git -C super mktree <tree.new) &&
++	commit=$(echo with space | git -C super commit-tree $tree) &&
++	git -C super update-ref refs/heads/master $commit &&
++
++	test_must_fail git clone --recurse-submodules super dst 2>err &&
++	test_i18ngrep "sub " err
++'
++
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0026-mingw-refuse-to-access-paths-with-illegal-characters.diff b/debian/patches/0026-mingw-refuse-to-access-paths-with-illegal-characters.diff
new file mode 100644
index 0000000..efc2627
--- /dev/null
+++ b/debian/patches/0026-mingw-refuse-to-access-paths-with-illegal-characters.diff
@@ -0,0 +1,106 @@
+From 9198107f367183b53c8bdc44c917bc334f09e21f Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Thu, 5 Sep 2019 13:44:21 +0200
+Subject: mingw: refuse to access paths with illegal characters
+
+Certain characters are not admissible in file names on Windows, even if
+Cygwin/MSYS2 (and therefore, Git for Windows' Bash) pretend that they
+are, e.g. `:`, `<`, `>`, etc
+
+Let's disallow those characters explicitly in Windows builds of Git.
+
+Note: just like trailing spaces or periods, it _is_ possible on Windows
+to create commits adding files with such illegal characters, as long as
+the operation leaves the worktree untouched. To allow for that, we
+continue to guard `is_valid_win32_path()` behind the config setting
+`core.protectNTFS`, so that users _can_ continue to do that, as long as
+they turn the protections off via that config setting.
+
+Among other problems, this prevents Git from trying to write to an "NTFS
+Alternate Data Stream" (which refers to metadata stored alongside a
+file, under a special name: "<filename>:<stream-name>"). This fix
+therefore also prevents an attack vector that was exploited in
+demonstrations of a number of recently-fixed security bugs.
+
+Further reading on illegal characters in Win32 filenames:
+https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 817ddd64c20b29b2d86b3a0589f7ff88d1279109)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ compat/mingw.c        | 10 ++++++++++
+ compat/mingw.h        |  7 +++++--
+ t/t0060-path-utils.sh |  4 +++-
+ 3 files changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/compat/mingw.c b/compat/mingw.c
+index 386aa94acb..806822744f 100644
+--- a/compat/mingw.c
++++ b/compat/mingw.c
+@@ -2447,6 +2447,8 @@ int is_valid_win32_path(const char *path)
+ 	if (!protect_ntfs)
+ 		return 1;
+ 
++	skip_dos_drive_prefix((char **)&path);
++
+ 	for (;;) {
+ 		char c = *(path++);
+ 		switch (c) {
+@@ -2468,6 +2470,14 @@ int is_valid_win32_path(const char *path)
+ 			preceding_space_or_period = 1;
+ 			i++;
+ 			continue;
++		case ':': /* DOS drive prefix was already skipped */
++		case '<': case '>': case '"': case '|': case '?': case '*':
++			/* illegal character */
++			return 0;
++		default:
++			if (c > '\0' && c < '\x20')
++				/* illegal character */
++				return 0;
+ 		}
+ 		preceding_space_or_period = 0;
+ 		i++;
+diff --git a/compat/mingw.h b/compat/mingw.h
+index 1d7e9d7c9d..38fee3482e 100644
+--- a/compat/mingw.h
++++ b/compat/mingw.h
+@@ -482,8 +482,11 @@ extern char *mingw_query_user_email(void);
+ /**
+  * Verifies that the given path is a valid one on Windows.
+  *
+- * In particular, path segments are disallowed which end in a period or a
+- * space (except the special directories `.` and `..`).
++ * In particular, path segments are disallowed which
++ *
++ * - end in a period or a space (except the special directories `.` and `..`).
++ *
++ * - contain any of the reserved characters, e.g. `:`, `;`, `*`, etc
+  *
+  * Returns 1 upon success, otherwise 0.
+  */
+diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
+index dc245c7dfe..2052583489 100755
+--- a/t/t0060-path-utils.sh
++++ b/t/t0060-path-utils.sh
+@@ -460,13 +460,15 @@ test_expect_success MINGW 'is_valid_path() on Windows' '
+ 		win32 \
+ 		"win32 x" \
+ 		../hello.txt \
++		C:\\git \
+ 		\
+ 		--not \
+ 		"win32 "  \
+ 		"win32 /x "  \
+ 		"win32."  \
+ 		"win32 . ." \
+-		.../hello.txt
++		.../hello.txt \
++		colon:test
+ '
+ 
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0027-mingw-handle-subst-ed-DOS-drives.diff b/debian/patches/0027-mingw-handle-subst-ed-DOS-drives.diff
new file mode 100644
index 0000000..3143af2
--- /dev/null
+++ b/debian/patches/0027-mingw-handle-subst-ed-DOS-drives.diff
@@ -0,0 +1,141 @@
+From f04fc7fb8aa28cbe0702949677d35fffdf23f3b7 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Fri, 6 Sep 2019 00:09:10 +0200
+Subject: mingw: handle `subst`-ed "DOS drives"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Over a decade ago, in 25fe217b86c (Windows: Treat Windows style path
+names., 2008-03-05), Git was taught to handle absolute Windows paths,
+i.e. paths that start with a drive letter and a colon.
+
+Unbeknownst to us, while drive letters of physical drives are limited to
+letters of the English alphabet, there is a way to assign virtual drive
+letters to arbitrary directories, via the `subst` command, which is
+_not_ limited to English letters.
+
+It is therefore possible to have absolute Windows paths of the form
+`1:\what\the\hex.txt`. Even "better": pretty much arbitrary Unicode
+letters can also be used, e.g. `ä:\tschibät.sch`.
+
+While it can be sensibly argued that users who set up such funny drive
+letters really seek adverse consequences, the Windows Operating System
+is known to be a platform where many users are at the mercy of
+administrators who have their very own idea of what constitutes a
+reasonable setup.
+
+Therefore, let's just make sure that such funny paths are still
+considered absolute paths by Git, on Windows.
+
+In addition to Unicode characters, pretty much any character is a valid
+drive letter, as far as `subst` is concerned, even `:` and `"` or even a
+space character. While it is probably the opposite of smart to use them,
+let's safeguard `is_dos_drive_prefix()` against all of them.
+
+Note: `[::1]:repo` is a valid URL, but not a valid path on Windows.
+As `[` is now considered a valid drive letter, we need to be very
+careful to avoid misinterpreting such a string as valid local path in
+`url_is_local_not_ssh()`. To do that, we use the just-introduced
+function `is_valid_path()` (which will label the string as invalid file
+name because of the colon characters).
+
+This fixes CVE-2019-1351.
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit f82a97eb9197c1e3768e72648f37ce0ca3233734)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ compat/mingw.c        | 24 ++++++++++++++++++++++++
+ compat/mingw.h        |  4 ++--
+ connect.c             |  2 +-
+ t/t0060-path-utils.sh |  9 +++++++++
+ 4 files changed, 36 insertions(+), 3 deletions(-)
+
+diff --git a/compat/mingw.c b/compat/mingw.c
+index 806822744f..a010e0b69d 100644
+--- a/compat/mingw.c
++++ b/compat/mingw.c
+@@ -2299,6 +2299,30 @@ pid_t waitpid(pid_t pid, int *status, int options)
+ 	return -1;
+ }
+ 
++int mingw_has_dos_drive_prefix(const char *path)
++{
++	int i;
++
++	/*
++	 * Does it start with an ASCII letter (i.e. highest bit not set),
++	 * followed by a colon?
++	 */
++	if (!(0x80 & (unsigned char)*path))
++		return *path && path[1] == ':' ? 2 : 0;
++
++	/*
++	 * While drive letters must be letters of the English alphabet, it is
++	 * possible to assign virtually _any_ Unicode character via `subst` as
++	 * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
++	 * like this:
++	 *
++	 *      subst ֍: %USERPROFILE%\Desktop
++	 */
++	for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
++		; /* skip first UTF-8 character */
++	return path[i] == ':' ? i + 1 : 0;
++}
++
+ int mingw_skip_dos_drive_prefix(char **path)
+ {
+ 	int ret = has_dos_drive_prefix(*path);
+diff --git a/compat/mingw.h b/compat/mingw.h
+index 38fee3482e..163ae1b59e 100644
+--- a/compat/mingw.h
++++ b/compat/mingw.h
+@@ -443,8 +443,8 @@ HANDLE winansi_get_osfhandle(int fd);
+  * git specific compatibility
+  */
+ 
+-#define has_dos_drive_prefix(path) \
+-	(isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
++int mingw_has_dos_drive_prefix(const char *path);
++#define has_dos_drive_prefix mingw_has_dos_drive_prefix
+ int mingw_skip_dos_drive_prefix(char **path);
+ #define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
+ static inline int mingw_is_dir_sep(int c)
+diff --git a/connect.c b/connect.c
+index 24281b6082..79f1b3b242 100644
+--- a/connect.c
++++ b/connect.c
+@@ -514,7 +514,7 @@ int url_is_local_not_ssh(const char *url)
+ 	const char *colon = strchr(url, ':');
+ 	const char *slash = strchr(url, '/');
+ 	return !colon || (slash && slash < colon) ||
+-		has_dos_drive_prefix(url);
++		(has_dos_drive_prefix(url) && is_valid_path(url));
+ }
+ 
+ static const char *prot_name(enum protocol protocol)
+diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
+index 2052583489..b193ed4205 100755
+--- a/t/t0060-path-utils.sh
++++ b/t/t0060-path-utils.sh
+@@ -165,6 +165,15 @@ test_expect_success 'absolute path rejects the empty string' '
+ 	test_must_fail test-tool path-utils absolute_path ""
+ '
+ 
++test_expect_success MINGW '<drive-letter>:\\abc is an absolute path' '
++	for letter in : \" C Z 1 ä
++	do
++		path=$letter:\\abc &&
++		absolute="$(test-tool path-utils absolute_path "$path")" &&
++		test "$path" = "$absolute" || return 1
++	done
++'
++
+ test_expect_success 'real path rejects the empty string' '
+ 	test_must_fail test-tool path-utils real_path ""
+ '
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0028-Git-2.14.6.diff b/debian/patches/0028-Git-2.14.6.diff
new file mode 100644
index 0000000..6d62056
--- /dev/null
+++ b/debian/patches/0028-Git-2.14.6.diff
@@ -0,0 +1,76 @@
+From fb5fb91f7fe254da465782a9dae722ac7be19a1f Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 19:58:46 +0100
+Subject: Git 2.14.6
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 66d2a6159f511924e7e0b8a21c93538879bfd622)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.14.6.txt | 54 +++++++++++++++++++++++++++++++
+ 1 file changed, 54 insertions(+)
+ create mode 100644 Documentation/RelNotes/2.14.6.txt
+
+diff --git a/Documentation/RelNotes/2.14.6.txt b/Documentation/RelNotes/2.14.6.txt
+new file mode 100644
+index 0000000000..72b7af6799
+--- /dev/null
++++ b/Documentation/RelNotes/2.14.6.txt
+@@ -0,0 +1,54 @@
++Git v2.14.6 Release Notes
++=========================
++
++This release addresses the security issues CVE-2019-1348,
++CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
++CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387.
++
++Fixes since v2.14.5
++-------------------
++
++ * CVE-2019-1348:
++   The --export-marks option of git fast-import is exposed also via
++   the in-stream command feature export-marks=... and it allows
++   overwriting arbitrary paths.
++
++ * CVE-2019-1349:
++   When submodules are cloned recursively, under certain circumstances
++   Git could be fooled into using the same Git directory twice. We now
++   require the directory to be empty.
++
++ * CVE-2019-1350:
++   Incorrect quoting of command-line arguments allowed remote code
++   execution during a recursive clone in conjunction with SSH URLs.
++
++ * CVE-2019-1351:
++   While the only permitted drive letters for physical drives on
++   Windows are letters of the US-English alphabet, this restriction
++   does not apply to virtual drives assigned via subst <letter>:
++   <path>. Git mistook such paths for relative paths, allowing writing
++   outside of the worktree while cloning.
++
++ * CVE-2019-1352:
++   Git was unaware of NTFS Alternate Data Streams, allowing files
++   inside the .git/ directory to be overwritten during a clone.
++
++ * CVE-2019-1353:
++   When running Git in the Windows Subsystem for Linux (also known as
++   "WSL") while accessing a working directory on a regular Windows
++   drive, none of the NTFS protections were active.
++
++ * CVE-2019-1354:
++   Filenames on Linux/Unix can contain backslashes. On Windows,
++   backslashes are directory separators. Git did not use to refuse to
++   write out tracked files with such filenames.
++
++ * CVE-2019-1387:
++   Recursive clones are currently affected by a vulnerability that is
++   caused by too-lax validation of submodule names, allowing very
++   targeted attacks via remote code execution in recursive clones.
++
++Credit for finding these vulnerabilities goes to Microsoft Security
++Response Center, in particular to Nicolas Joly. The `fast-import`
++fixes were provided by Jeff King, the other fixes by Johannes
++Schindelin with help from Garima Singh.
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0029-submodule-reject-submodule.update-command-in-.gitmodu.diff b/debian/patches/0029-submodule-reject-submodule.update-command-in-.gitmodu.diff
new file mode 100644
index 0000000..dc73579
--- /dev/null
+++ b/debian/patches/0029-submodule-reject-submodule.update-command-in-.gitmodu.diff
@@ -0,0 +1,152 @@
+From 8dca1221839cf6054fb05d63d2049869600ed672 Mon Sep 17 00:00:00 2001
+From: Jonathan Nieder <jrnieder@gmail.com>
+Date: Thu, 5 Dec 2019 01:28:28 -0800
+Subject: submodule: reject submodule.update = !command in .gitmodules
+
+Since ac1fbbda2013 (submodule: do not copy unknown update mode from
+.gitmodules, 2013-12-02), Git has been careful to avoid copying
+
+	[submodule "foo"]
+		update = !run an arbitrary scary command
+
+from .gitmodules to a repository's local config, copying in the
+setting 'update = none' instead.  The gitmodules(5) manpage documents
+the intention:
+
+	The !command form is intentionally ignored here for security
+	reasons
+
+Unfortunately, starting with v2.20.0-rc0 (which integrated ee69b2a9
+(submodule--helper: introduce new update-module-mode helper,
+2018-08-13, first released in v2.20.0-rc0)), there are scenarios where
+we *don't* ignore it: if the config store contains no
+submodule.foo.update setting, the submodule-config API falls back to
+reading .gitmodules and the repository-supplied !command gets run
+after all.
+
+This was part of a general change over time in submodule support to
+read more directly from .gitmodules, since unlike .git/config it
+allows a project to change values between branches and over time
+(while still allowing .git/config to override things).  But it was
+never intended to apply to this kind of dangerous configuration.
+
+The behavior change was not advertised in ee69b2a9's commit message
+and was missed in review.
+
+Let's take the opportunity to make the protection more robust, even in
+Git versions that are technically not affected: instead of quietly
+converting 'update = !command' to 'update = none', noisily treat it as
+an error.  Allowing the setting but treating it as meaning something
+else was just confusing; users are better served by seeing the error
+sooner.  Forbidding the construct makes the semantics simpler and
+means we can check for it in fsck (in a separate patch).
+
+As a result, the submodule-config API cannot read this value from
+.gitmodules under any circumstance, and we can declare with confidence
+
+	For security reasons, the '!command' form is not accepted
+	here.
+
+Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
+(cherry picked from commit e904deb89d9a9669a76a426182506a084d3f6308)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/gitmodules.txt |  5 ++---
+ submodule-config.c           | 12 ++++++++++--
+ t/t7406-submodule-update.sh  | 14 ++++++++------
+ 3 files changed, 20 insertions(+), 11 deletions(-)
+
+diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
+index 312b6f9259..164995d1af 100644
+--- a/Documentation/gitmodules.txt
++++ b/Documentation/gitmodules.txt
+@@ -44,9 +44,8 @@ submodule.<name>.update::
+ 	submodule init` to initialize the configuration variable of
+ 	the same name. Allowed values here are 'checkout', 'rebase',
+ 	'merge' or 'none'. See description of 'update' command in
+-	linkgit:git-submodule[1] for their meaning. Note that the
+-	'!command' form is intentionally ignored here for security
+-	reasons.
++	linkgit:git-submodule[1] for their meaning. For security
++	reasons, the '!command' form is not accepted here.
+ 
+ submodule.<name>.branch::
+ 	A remote branch name for tracking updates in the upstream submodule.
+diff --git a/submodule-config.c b/submodule-config.c
+index 52702c62d9..8c00855ed8 100644
+--- a/submodule-config.c
++++ b/submodule-config.c
+@@ -398,6 +398,13 @@ struct parse_config_parameter {
+ 	int overwrite;
+ };
+ 
++/*
++ * Parse a config item from .gitmodules.
++ *
++ * This does not handle submodule-related configuration from the main
++ * config store (.git/config, etc).  Callers are responsible for
++ * checking for overrides in the main config store when appropriate.
++ */
+ static int parse_config(const char *var, const char *value, void *data)
+ {
+ 	struct parse_config_parameter *me = data;
+@@ -475,8 +482,9 @@ static int parse_config(const char *var, const char *value, void *data)
+ 			warn_multiple_config(me->treeish_name, submodule->name,
+ 					     "update");
+ 		else if (parse_submodule_update_strategy(value,
+-			 &submodule->update_strategy) < 0)
+-				die(_("invalid value for %s"), var);
++			 &submodule->update_strategy) < 0 ||
++			 submodule->update_strategy.type == SM_UPDATE_COMMAND)
++			die(_("invalid value for %s"), var);
+ 	} else if (!strcmp(item.buf, "shallow")) {
+ 		if (!me->overwrite && submodule->recommend_shallow != -1)
+ 			warn_multiple_config(me->treeish_name, submodule->name,
+diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
+index e87164aa8f..778352e313 100755
+--- a/t/t7406-submodule-update.sh
++++ b/t/t7406-submodule-update.sh
+@@ -407,12 +407,12 @@ test_expect_success 'submodule update - command in .git/config' '
+ 	)
+ '
+ 
+-test_expect_success 'submodule update - command in .gitmodules is ignored' '
++test_expect_success 'submodule update - command in .gitmodules is rejected' '
+ 	test_when_finished "git -C super reset --hard HEAD^" &&
+ 	git -C super config -f .gitmodules submodule.submodule.update "!false" &&
+ 	git -C super commit -a -m "add command to .gitmodules file" &&
+ 	git -C super/submodule reset --hard $submodulesha1^ &&
+-	git -C super submodule update submodule
++	test_must_fail git -C super submodule update submodule
+ '
+ 
+ cat << EOF >expect
+@@ -481,6 +481,9 @@ test_expect_success 'recursive submodule update - command in .git/config catches
+ '
+ 
+ test_expect_success 'submodule init does not copy command into .git/config' '
++	test_when_finished "git -C super update-index --force-remove submodule1" &&
++	test_when_finished git config -f super/.gitmodules \
++		--remove-section submodule.submodule1 &&
+ 	(cd super &&
+ 	 git ls-files -s submodule >out &&
+ 	 H=$(cut -d" " -f2 out) &&
+@@ -489,10 +492,9 @@ test_expect_success 'submodule init does not copy command into .git/config' '
+ 	 git config -f .gitmodules submodule.submodule1.path submodule1 &&
+ 	 git config -f .gitmodules submodule.submodule1.url ../submodule &&
+ 	 git config -f .gitmodules submodule.submodule1.update !false &&
+-	 git submodule init submodule1 &&
+-	 echo "none" >expect &&
+-	 git config submodule.submodule1.update >actual &&
+-	 test_cmp expect actual
++	 test_must_fail git submodule init submodule1 &&
++	 test_expect_code 1 git config submodule.submodule1.update >actual &&
++	 test_must_be_empty actual
+ 	)
+ '
+ 
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0030-Git-2.15.4.diff b/debian/patches/0030-Git-2.15.4.diff
new file mode 100644
index 0000000..e5b86ff
--- /dev/null
+++ b/debian/patches/0030-Git-2.15.4.diff
@@ -0,0 +1,33 @@
+From a2a23f0ba9f2efff7131cbe73ecf8bed5465166b Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 21:33:29 +0100
+Subject: Git 2.15.4
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 7cdafcaacf677b9e0700fa988c247bda192db48d)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.15.4.txt | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+ create mode 100644 Documentation/RelNotes/2.15.4.txt
+
+diff --git a/Documentation/RelNotes/2.15.4.txt b/Documentation/RelNotes/2.15.4.txt
+new file mode 100644
+index 0000000000..dc241cba34
+--- /dev/null
++++ b/Documentation/RelNotes/2.15.4.txt
+@@ -0,0 +1,11 @@
++Git v2.15.4 Release Notes
++=========================
++
++This release merges up the fixes that appear in v2.14.6 to address
++the security issues CVE-2019-1348, CVE-2019-1349, CVE-2019-1350,
++CVE-2019-1351, CVE-2019-1352, CVE-2019-1353, CVE-2019-1354, and
++CVE-2019-1387; see the release notes for that version for details.
++
++In conjunction with a vulnerability that was fixed in v2.20.2,
++`.gitmodules` is no longer allowed to contain entries of the form
++`submodule.<name>.update=!command`.
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0031-test-drop-caches-use-has_dos_drive_prefix.diff b/debian/patches/0031-test-drop-caches-use-has_dos_drive_prefix.diff
new file mode 100644
index 0000000..4377cfc
--- /dev/null
+++ b/debian/patches/0031-test-drop-caches-use-has_dos_drive_prefix.diff
@@ -0,0 +1,50 @@
+From 74f9bd294505a878d4c991daa2671d530a263723 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 21:40:01 +0100
+Subject: test-drop-caches: use `has_dos_drive_prefix()`
+
+This is a companion patch to 'mingw: handle `subst`-ed "DOS drives"':
+use the DOS drive prefix handling that is already provided by
+`compat/mingw.c` (and which just learned to handle non-alphabetical
+"drive letters").
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 68440496c77c6d3a606537c78ea4b62eb895a64a)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/helper/test-drop-caches.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
+index f65e301f9d..7b4278462b 100644
+--- a/t/helper/test-drop-caches.c
++++ b/t/helper/test-drop-caches.c
+@@ -8,18 +8,21 @@ static int cmd_sync(void)
+ {
+ 	char Buffer[MAX_PATH];
+ 	DWORD dwRet;
+-	char szVolumeAccessPath[] = "\\\\.\\X:";
++	char szVolumeAccessPath[] = "\\\\.\\XXXX:";
+ 	HANDLE hVolWrite;
+-	int success = 0;
++	int success = 0, dos_drive_prefix;
+ 
+ 	dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
+ 	if ((0 == dwRet) || (dwRet > MAX_PATH))
+ 		return error("Error getting current directory");
+ 
+-	if (!has_dos_drive_prefix(Buffer))
++	dos_drive_prefix = has_dos_drive_prefix(Buffer);
++	if (!dos_drive_prefix)
+ 		return error("'%s': invalid drive letter", Buffer);
+ 
+-	szVolumeAccessPath[4] = Buffer[0];
++	memcpy(szVolumeAccessPath, Buffer, dos_drive_prefix);
++	szVolumeAccessPath[dos_drive_prefix] = '\0';
++
+ 	hVolWrite = CreateFile(szVolumeAccessPath, GENERIC_READ | GENERIC_WRITE,
+ 		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ 	if (INVALID_HANDLE_VALUE == hVolWrite)
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0032-Git-2.16.6.diff b/debian/patches/0032-Git-2.16.6.diff
new file mode 100644
index 0000000..d1c93cb
--- /dev/null
+++ b/debian/patches/0032-Git-2.16.6.diff
@@ -0,0 +1,30 @@
+From 842fb2283fd917101c0bfe72a9e40f945a377ce1 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 21:45:07 +0100
+Subject: Git 2.16.6
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit eb288bc455ac67e3ceeff90daf6f25972bb586d0)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.16.6.txt | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+ create mode 100644 Documentation/RelNotes/2.16.6.txt
+
+diff --git a/Documentation/RelNotes/2.16.6.txt b/Documentation/RelNotes/2.16.6.txt
+new file mode 100644
+index 0000000000..438306e60b
+--- /dev/null
++++ b/Documentation/RelNotes/2.16.6.txt
+@@ -0,0 +1,8 @@
++Git v2.16.6 Release Notes
++=========================
++
++This release merges up the fixes that appear in v2.14.6 and in
++v2.15.4 addressing the security issues CVE-2019-1348, CVE-2019-1349,
++CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
++CVE-2019-1354, and CVE-2019-1387; see the release notes for those
++versions for details.
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0033-fsck-reject-submodule.update-command-in-.gitmodules.diff b/debian/patches/0033-fsck-reject-submodule.update-command-in-.gitmodules.diff
new file mode 100644
index 0000000..223a188
--- /dev/null
+++ b/debian/patches/0033-fsck-reject-submodule.update-command-in-.gitmodules.diff
@@ -0,0 +1,78 @@
+From 4fba32d885ad8016c96860b75262a3f377791940 Mon Sep 17 00:00:00 2001
+From: Jonathan Nieder <jrnieder@gmail.com>
+Date: Thu, 5 Dec 2019 01:30:43 -0800
+Subject: fsck: reject submodule.update = !command in .gitmodules
+
+This allows hosting providers to detect whether they are being used
+to attack users using malicious 'update = !command' settings in
+.gitmodules.
+
+Since ac1fbbda2013 (submodule: do not copy unknown update mode from
+.gitmodules, 2013-12-02), in normal cases such settings have been
+treated as 'update = none', so forbidding them should not produce any
+collateral damage to legitimate uses.  A quick search does not reveal
+any repositories making use of this construct, either.
+
+Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit bb92255ebe6bccd76227e023d6d0bc997e318ad0)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fsck.c                      |  7 +++++++
+ t/t7406-submodule-update.sh | 14 ++++++++++++++
+ 2 files changed, 21 insertions(+)
+
+diff --git a/fsck.c b/fsck.c
+index b0526dd2b6..535f806c67 100644
+--- a/fsck.c
++++ b/fsck.c
+@@ -68,6 +68,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
+ 	FUNC(GITMODULES_SYMLINK, ERROR) \
+ 	FUNC(GITMODULES_URL, ERROR) \
+ 	FUNC(GITMODULES_PATH, ERROR) \
++	FUNC(GITMODULES_UPDATE, ERROR) \
+ 	/* warnings */ \
+ 	FUNC(BAD_FILEMODE, WARN) \
+ 	FUNC(EMPTY_NAME, WARN) \
+@@ -1016,6 +1017,12 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+ 				    FSCK_MSG_GITMODULES_PATH,
+ 				    "disallowed submodule path: %s",
+ 				    value);
++	if (!strcmp(key, "update") && value &&
++	    parse_submodule_update_type(value) == SM_UPDATE_COMMAND)
++		data->ret |= report(data->options, data->obj,
++				    FSCK_MSG_GITMODULES_UPDATE,
++				    "disallowed submodule update setting: %s",
++				    value);
+ 	free(name);
+ 
+ 	return 0;
+diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
+index 778352e313..ad7d8fa69e 100755
+--- a/t/t7406-submodule-update.sh
++++ b/t/t7406-submodule-update.sh
+@@ -415,6 +415,20 @@ test_expect_success 'submodule update - command in .gitmodules is rejected' '
+ 	test_must_fail git -C super submodule update submodule
+ '
+ 
++test_expect_success 'fsck detects command in .gitmodules' '
++	git init command-in-gitmodules &&
++	(
++		cd command-in-gitmodules &&
++		git submodule add ../submodule submodule &&
++		test_commit adding-submodule &&
++
++		git config -f .gitmodules submodule.submodule.update "!false" &&
++		git add .gitmodules &&
++		test_commit configuring-update &&
++		test_must_fail git fsck
++	)
++'
++
+ cat << EOF >expect
+ Execution of 'false $submodulesha1' failed in submodule path 'submodule'
+ EOF
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0034-Git-2.17.3.diff b/debian/patches/0034-Git-2.17.3.diff
new file mode 100644
index 0000000..5116586
--- /dev/null
+++ b/debian/patches/0034-Git-2.17.3.diff
@@ -0,0 +1,34 @@
+From 6c06b6f10d09a4dd639b06da47751157eb472b55 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 22:13:04 +0100
+Subject: Git 2.17.3
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit a5ab8d03173458b76b8452efd90a7173f490c132)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.17.3.txt | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+ create mode 100644 Documentation/RelNotes/2.17.3.txt
+
+diff --git a/Documentation/RelNotes/2.17.3.txt b/Documentation/RelNotes/2.17.3.txt
+new file mode 100644
+index 0000000000..5a46c94271
+--- /dev/null
++++ b/Documentation/RelNotes/2.17.3.txt
+@@ -0,0 +1,12 @@
++Git v2.17.3 Release Notes
++=========================
++
++This release merges up the fixes that appear in v2.14.6 and in
++v2.15.4 addressing the security issues CVE-2019-1348, CVE-2019-1349,
++CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
++CVE-2019-1354, and CVE-2019-1387; see the release notes for those
++versions for details.
++
++In addition, `git fsck` was taught to identify `.gitmodules` entries
++of the form `submodule.<name>.update=!command`, which have been
++disallowed in v2.15.4.
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0035-Git-2.18.2.diff b/debian/patches/0035-Git-2.18.2.diff
new file mode 100644
index 0000000..7cc103d
--- /dev/null
+++ b/debian/patches/0035-Git-2.18.2.diff
@@ -0,0 +1,30 @@
+From d5659c183c956d392ab61c2241942405dab9ad9f Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 22:22:52 +0100
+Subject: Git 2.18.2
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 9877106b01cbd346b862cc8cd2c52e496dd40ed5)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.18.2.txt | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+ create mode 100644 Documentation/RelNotes/2.18.2.txt
+
+diff --git a/Documentation/RelNotes/2.18.2.txt b/Documentation/RelNotes/2.18.2.txt
+new file mode 100644
+index 0000000000..98b168aade
+--- /dev/null
++++ b/Documentation/RelNotes/2.18.2.txt
+@@ -0,0 +1,8 @@
++Git v2.18.2 Release Notes
++=========================
++
++This release merges up the fixes that appear in v2.14.6, v2.15.4
++and in v2.17.3, addressing the security issues CVE-2019-1348,
++CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
++CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
++for those versions for details.
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0036-Git-2.19.3.diff b/debian/patches/0036-Git-2.19.3.diff
new file mode 100644
index 0000000..28610c2
--- /dev/null
+++ b/debian/patches/0036-Git-2.19.3.diff
@@ -0,0 +1,30 @@
+From 374d46b817c9f58ef68aca941b137f4d6744c093 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 22:29:33 +0100
+Subject: Git 2.19.3
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit caccc527ca7f4b3e6f4bb6775cbff94b27741482)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.19.3.txt | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+ create mode 100644 Documentation/RelNotes/2.19.3.txt
+
+diff --git a/Documentation/RelNotes/2.19.3.txt b/Documentation/RelNotes/2.19.3.txt
+new file mode 100644
+index 0000000000..92d7f89de6
+--- /dev/null
++++ b/Documentation/RelNotes/2.19.3.txt
+@@ -0,0 +1,8 @@
++Git v2.19.3 Release Notes
++=========================
++
++This release merges up the fixes that appear in v2.14.6, v2.15.4
++and in v2.17.3, addressing the security issues CVE-2019-1348,
++CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
++CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
++for those versions for details.
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0037-t7415-adjust-test-for-dubiously-nested-submodule-gitd.diff b/debian/patches/0037-t7415-adjust-test-for-dubiously-nested-submodule-gitd.diff
new file mode 100644
index 0000000..9fde9d9
--- /dev/null
+++ b/debian/patches/0037-t7415-adjust-test-for-dubiously-nested-submodule-gitd.diff
@@ -0,0 +1,48 @@
+From 104be35aea6fe4893482cbcd0f2077ef880306c2 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 10:06:08 +0100
+Subject: t7415: adjust test for dubiously-nested submodule gitdirs for v2.20.x
+
+In v2.20.x, Git clones submodules recursively by first creating the
+submodules' gitdirs and _then_ "updating" the submodules. This can lead
+to the situation where the clone path is taken because the directory
+(while it exists already) is not a git directory, but then the clone
+fails because that gitdir is unexpectedly already a directory.
+
+This _also_ works around the vulnerability that was fixed in "Disallow
+dubiously-nested submodule git directories", but it produces a different
+error message than the one expected by the test case, therefore we
+adjust the test case accordingly.
+
+Note: as the two submodules "race each other", there are actually two
+possible error messages, therefore we have to teach the test case to
+expect _two_ possible (and good) outcomes in addition to the one it
+expected before.
+
+Note: this workaround is only necessary for the v2.20.x release train;
+The behavior changed again in v2.21.x so that the original test case's
+expectations are met again.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 4cfc47de25be7be1cddb47dcfddab3f1f80e5c41)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t7415-submodule-names.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index 33a9126ee0..140ea8cb8d 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -244,7 +244,7 @@ test_expect_success 'git dirs of sibling submodules must not be nested' '
+ 		git commit -m nested
+ 	) &&
+ 	test_must_fail git clone --recurse-submodules nested clone 2>err &&
+-	test_i18ngrep "is inside git dir" err
++	test_i18ngrep -E "(is inside git dir|hippo already exists|not a git repository: .*/hippo)" err
+ '
+ 
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0038-submodule-defend-against-submodule.update-command-in-.diff b/debian/patches/0038-submodule-defend-against-submodule.update-command-in-.diff
new file mode 100644
index 0000000..45f11ea
--- /dev/null
+++ b/debian/patches/0038-submodule-defend-against-submodule.update-command-in-.diff
@@ -0,0 +1,33 @@
+From 7d4f5522400fa6b0a8f0c1d72cbcab6d0b74afc5 Mon Sep 17 00:00:00 2001
+From: Jonathan Nieder <jrnieder@gmail.com>
+Date: Thu, 5 Dec 2019 01:28:28 -0800
+Subject: submodule: defend against submodule.update = !command in .gitmodules
+
+In v2.15.4, we started to reject `submodule.update` settings in
+`.gitmodules`. Let's raise a BUG if it somehow still made it through
+from anywhere but the Git config.
+
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
+(cherry picked from commit c1547450748fcbac21675f2681506d2d80351a19)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ builtin/submodule--helper.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
+index b93d624a85..f26bdace0b 100644
+--- a/builtin/submodule--helper.c
++++ b/builtin/submodule--helper.c
+@@ -1482,6 +1482,8 @@ static void determine_submodule_update_strategy(struct repository *r,
+ 			die(_("Invalid update mode '%s' configured for submodule path '%s'"),
+ 				val, path);
+ 	} else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
++		if (sub->update_strategy.type == SM_UPDATE_COMMAND)
++			BUG("how did we read update = !command from .gitmodules?");
+ 		out->type = sub->update_strategy.type;
+ 		out->command = sub->update_strategy.command;
+ 	} else
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/0039-Git-2.20.2.diff b/debian/patches/0039-Git-2.20.2.diff
new file mode 100644
index 0000000..f22599b
--- /dev/null
+++ b/debian/patches/0039-Git-2.20.2.diff
@@ -0,0 +1,54 @@
+From afdc7c3c9874522c09de39680a6550b086f60a78 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 4 Dec 2019 22:33:15 +0100
+Subject: Git 2.20.2
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+(cherry picked from commit 4cd1cf31efed9b16db5035c377bfa222f5272458)
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/RelNotes/2.20.2.txt | 18 ++++++++++++++++++
+ GIT-VERSION-GEN                   |  2 +-
+ 2 files changed, 19 insertions(+), 1 deletions(-)
+ create mode 100644 Documentation/RelNotes/2.20.2.txt
+
+diff --git a/Documentation/RelNotes/2.20.2.txt b/Documentation/RelNotes/2.20.2.txt
+new file mode 100644
+index 0000000000..8e680cb9fb
+--- /dev/null
++++ b/Documentation/RelNotes/2.20.2.txt
+@@ -0,0 +1,18 @@
++Git v2.20.2 Release Notes
++=========================
++
++This release merges up the fixes that appear in v2.14.6, v2.15.4
++and in v2.17.3, addressing the security issues CVE-2019-1348,
++CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
++CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
++for those versions for details.
++
++The change to disallow `submodule.<name>.update=!command` entries in
++`.gitmodules` which was introduced v2.15.4 (and for which v2.17.3
++added explicit fsck checks) fixes the vulnerability in v2.20.x where a
++recursive clone followed by a submodule update could execute code
++contained within the repository without the user explicitly having
++asked for that (CVE-2019-19604).
++
++Credit for finding this vulnerability goes to Joern Schneeweisz,
++credit for the fixes goes to Jonathan Nieder.
+diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
+index d1a2814ec7..492f3480f0 100755
+--- a/GIT-VERSION-GEN
++++ b/GIT-VERSION-GEN
+@@ -1,7 +1,7 @@
+ #!/bin/sh
+ 
+ GVF=GIT-VERSION-FILE
+-DEF_VER=v2.20.1
++DEF_VER=v2.20.2
+ 
+ LF='
+ '
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..b677e7e
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,39 @@
+0001-t9300-drop-some-useless-uses-of-cat.diff
+0002-t9300-create-marks-files-for-double-import-marks-test.diff
+0003-fast-import-tighten-parsing-of-boolean-command-line-o.diff
+0004-fast-import-stop-creating-leading-directories-for-imp.diff
+0005-fast-import-delay-creating-leading-directories-for-ex.diff
+0006-fast-import-disallow-feature-export-marks-by-default.diff
+0007-fast-import-disallow-feature-import-marks-by-default.diff
+0008-clone-recurse-submodules-prevent-name-squatting-on-Wi.diff
+0009-mingw-disallow-backslash-characters-in-tree-objects-f.diff
+0010-path.c-document-the-purpose-of-is_ntfs_dotgit.diff
+0011-test-path-utils-offer-to-run-a-protectNTFS-protectHFS.diff
+0012-is_ntfs_dotgit-only-verify-the-leading-segment.diff
+0013-path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff
+0014-path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff
+0015-is_ntfs_dotgit-speed-it-up.diff
+0016-protect_ntfs-turn-on-NTFS-protection-by-default.diff
+0017-Disallow-dubiously-nested-submodule-git-directories.diff
+0018-mingw-fix-quoting-of-arguments.diff
+0019-tests-add-a-helper-to-stress-test-argument-quoting.diff
+0020-quote-stress-test-accept-arguments-to-test-via-the-co.diff
+0021-quote-stress-test-allow-skipping-some-trials.diff
+0022-quote-stress-test-offer-to-test-quoting-arguments-for.diff
+0023-t6130-t9350-prepare-for-stringent-Win32-path-validati.diff
+0024-unpack-trees-let-merged_entry-pass-through-do_add_ent.diff
+0025-mingw-refuse-to-access-paths-with-trailing-spaces-or-.diff
+0026-mingw-refuse-to-access-paths-with-illegal-characters.diff
+0027-mingw-handle-subst-ed-DOS-drives.diff
+0028-Git-2.14.6.diff
+0029-submodule-reject-submodule.update-command-in-.gitmodu.diff
+0030-Git-2.15.4.diff
+0031-test-drop-caches-use-has_dos_drive_prefix.diff
+0032-Git-2.16.6.diff
+0033-fsck-reject-submodule.update-command-in-.gitmodules.diff
+0034-Git-2.17.3.diff
+0035-Git-2.18.2.diff
+0036-Git-2.19.3.diff
+0037-t7415-adjust-test-for-dubiously-nested-submodule-gitd.diff
+0038-submodule-defend-against-submodule.update-command-in-.diff
+0039-Git-2.20.2.diff