From c1d8461ee34c6d3f987e0f19191f2105cb2a33c8 Mon Sep 17 00:00:00 2001
From: Elijah Newren <newren@gmail.com>
Date: Fri, 25 Jul 2025 21:24:54 -0700
Subject: [PATCH] filter-repo: fix --replace-text for python3.14

Our handling of globs for --replace-text makes use of fnmatch.translate
from the python standard library.  Unfortunately, fnmatch.translate
doesn't just give a regex that can match the given glob somewhere, it
gives a regex that will only match if the string it is comparing to is
exactly that glob.  We need a substring search, though, so we have to
use an ugly hack to butcher the returned regex from fnmatch to get it
to be what we want.  (It would be nice if python's fnmatch.translate()
took options for what was wanted, but it doesn't.)  This is fine, except
that...

python3.14 added '\z' as a synonym for '\Z' in regexes.  No special
reason, they just wanted there to be more than one way to do it.

Naturally, fnmatch.translate() uses '\z' instead of '\Z', so our regex
hackery in glob_to_regex() wasn't looking for the right stuff to hack
off, causing the globs to fail to match text as expected.

Add a python >= 3.14 hack to the existing python variation hacks in
glob_to_regex() so we can handle this case too.

While at it, the --replace-text test in t9394 did replacements on a
literal, a glob, and a regex, but it only verified that the glob and
regex replacements worked.  Supplement it with a check that the
literal replacement worked too.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 git-filter-repo                                            | 3 +++
 t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/git-filter-repo b/git-filter-repo
index 39c8680a..fb3de42e 100755
--- a/git-filter-repo
+++ b/git-filter-repo
@@ -154,6 +154,9 @@ def glob_to_regex(glob_bytestr):
     regex = regex[0:-7]
   elif regex.startswith(r'(?s:') and regex.endswith(r')\Z'): # pragma: no cover
     regex = regex[4:-3]
+  elif regex.startswith(r'(?s:') and regex.endswith(r')\z'): # pragma: no cover
+    # Yaay, python3.14 for senselessly duplicating \Z as \z...
+    regex = regex[4:-3]
 
   # Finally, convert back to regex operating on bytestr
   return regex.encode()
diff --git a/t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh b/t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh
index 0ff911db..5358cd5a 100755
--- a/t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh
+++ b/t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh
@@ -654,6 +654,9 @@ test_expect_success '--replace-text all options' '
 		git show HEAD~4:numbers/medium.num >actual &&
 		test_cmp expect actual &&
 
+		echo "foodstuff" >expect &&
+		test_cmp expect sequence/to &&
+
 		echo "haphazard ***REMOVED*** variation" >expect &&
 		test_cmp expect whatever
 	)
