user [email protected]
usertags 1140463 python3.15
tags 1140463 patch
thanks

Hi!

While rebuilding the python related packages against the Python 3.15rc2 version I ran into this FTBFS [1].
To make hg-git work with a newer dulwich, I had to add upstream patches to 
support dulwich 1.2 and mercurial 7.2 (17 patches in total)

I also added a patch to handle ChecksumMismatch raised by dulwich >= 1.2.7.

I've applied these fixes in the sandbox [2] to verify that it builds 
successfully, these should be applied in Debian to get the package back to a 
healthy state.

Happy hacking,

[1]: https://debusine.debian.net/debian/r-python-python3.15/artifact/4855744/
[2]: https://debusine.debian.net/debian/r-python-python3.15/

--
"Can you imagine what I would do if I could do all I can?" -- Sun Tzu
Saludos /\/\ /\ >< `/
diff --git a/debian/changelog b/debian/changelog
index 73b29420..43a3002c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+hg-git (1.2.0-3) UNRELEASED; urgency=medium
+
+  * Backport upstream fixes for Dulwich 1.2 and Mercurial 7.2
+    compatibility.
+  * Add patch dulwich-checksum-mismatch: fix gverify --fsck with Dulwich
+    >= 1.2.7.
+  * Drop from dulwich-compat the changes replaced by upstream backports.
+
+ -- Maximiliano Curia <[email protected]>  Mon, 28 Sep 2026 18:54:44 +0200
+
 hg-git (1.2.0-2) unstable; urgency=medium
 
   * Add patch dulwich-compat: fixes compatibility with Dulwich 0.24.
diff --git a/debian/control b/debian/control
index 36ddb865..af358c83 100644
--- a/debian/control
+++ b/debian/control
@@ -10,8 +10,8 @@ Build-Depends:
  git,
  openssh-client,
  python3,
- python3-dulwich (>= 0.24),
- python3-dulwich (<< 0.25),
+ python3-dulwich (>= 1.2.7),
+ python3-dulwich (<< 2),
  python3-mercurial,
  python3-setuptools,
  python3-setuptools-scm,
@@ -27,7 +27,7 @@ Package: mercurial-git
 Architecture: all
 Depends:
  python3-dulwich (>= 0.24),
- python3-dulwich (<< 0.25),
+ python3-dulwich (<< 2),
  python3-mercurial,
  ${misc:Depends},
  ${python3:Depends},
diff --git a/debian/patches/bundle-fix-test-bundle.t.-be-backward-compatible-in-a-ugly-w.patch b/debian/patches/bundle-fix-test-bundle.t.-be-backward-compatible-in-a-ugly-w.patch
new file mode 100644
index 00000000..56bf1457
--- /dev/null
+++ b/debian/patches/bundle-fix-test-bundle.t.-be-backward-compatible-in-a-ugly-w.patch
@@ -0,0 +1,78 @@
+From 1ec663f3767b625e3d80d79b20cd6b9aeca14a87 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Thu, 14 May 2026 00:57:06 +0200
+Subject: [PATCH] bundle: fix test-bundle.t. be backward compatible in a ugly
+ way
+
+hg 7.2 removed the "ui" argument from _addpartsfromopts() in 63c4874dacb6
+("bundle: remove unused argument")
+
+diff --git a/hggit/bundle.py b/hggit/bundle.py
+index fc8d0392..b6ba2ebd 100644
+--- a/hggit/bundle.py
++++ b/hggit/bundle.py
+@@ -1,3 +1,4 @@
++import inspect
+ import io
+ 
+ from mercurial import bundle2
+@@ -57,19 +58,46 @@ def addpartrevgittags(repo, bundler, outgoing):
+         bundler.newpart(BUNDLEPART_TAGS, data=chunks, mandatory=False)
+ 
+ 
[email protected](bundle2, '_addpartsfromopts')
+-def _addpartsfromopts(orig, ui, repo, bundler, source, outgoing, opts):
+-    orig(ui, repo, bundler, source, outgoing, opts)
+-
+-    if opts.get(CAPABILITY_MAP, False) or ui.configbool(
+-        b'experimental', b'hg-git-bundle'
+-    ):
+-        addpartrevgitmap(repo, bundler, outgoing)
+-
+-    if opts.get(CAPABILITY_TAGS, False) or ui.configbool(
+-        b'experimental', b'hg-git-bundle'
+-    ):
+-        addpartrevgittags(repo, bundler, outgoing)
++if inspect.getfullargspec(bundle2._addpartsfromopts).args == [
++    'repo',
++    'bundler',
++    'source',
++    'outgoing',
++    'opts',
++]:
++    # hg >= 7.2
++    #
++    # hg 7.2 removed the "ui" argument from _addpartsfromopts() in
++    # 63c4874dacb6 ("bundle: remove unused argument").
++    @eh.wrapfunction(bundle2, '_addpartsfromopts')
++    def _addpartsfromopts(orig, repo, bundler, source, outgoing, opts):
++        orig(repo, bundler, source, outgoing, opts)
++
++        if opts.get(CAPABILITY_MAP, False) or repo.ui.configbool(
++            b'experimental', b'hg-git-bundle'
++        ):
++            addpartrevgitmap(repo, bundler, outgoing)
++
++        if opts.get(CAPABILITY_TAGS, False) or repo.ui.configbool(
++            b'experimental', b'hg-git-bundle'
++        ):
++            addpartrevgittags(repo, bundler, outgoing)
++
++else:
++    # hg <= 7.1.2 still has the "ui" argument
++    @eh.wrapfunction(bundle2, '_addpartsfromopts')
++    def _addpartsfromopts(orig, ui, repo, bundler, source, outgoing, opts):
++        orig(ui, repo, bundler, source, outgoing, opts)
++
++        if opts.get(CAPABILITY_MAP, False) or ui.configbool(
++            b'experimental', b'hg-git-bundle'
++        ):
++            addpartrevgitmap(repo, bundler, outgoing)
++
++        if opts.get(CAPABILITY_TAGS, False) or ui.configbool(
++            b'experimental', b'hg-git-bundle'
++        ):
++            addpartrevgittags(repo, bundler, outgoing)
+ 
+ 
+ @eh.extsetup
diff --git a/debian/patches/bundle-fixe-test-hg-clone.t-and-test-serve-hg.t-with-hggit-o.patch b/debian/patches/bundle-fixe-test-hg-clone.t-and-test-serve-hg.t-with-hggit-o.patch
new file mode 100644
index 00000000..f8562a36
--- /dev/null
+++ b/debian/patches/bundle-fixe-test-hg-clone.t-and-test-serve-hg.t-with-hggit-o.patch
@@ -0,0 +1,45 @@
+From 61d2a1c2d127a16c744894537ee19d506c804379 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Thu, 14 May 2026 09:58:01 +0200
+Subject: [PATCH] bundle: fixe test-hg-clone.t and test-serve-hg.t#with-hggit
+ on hg >= 7.2
+
+bundle2.getrepocaps() was deprecated and will be removed in 7.3. The deprecation
+happened via util.deprecated(), which apparently breaks hggit wrapping.
+
+This change is backwards compatible (tested with hg 7.1.2 and 7.2.1).
+
+diff --git a/hggit/bundle.py b/hggit/bundle.py
+index b6ba2ebd..eceece8c 100644
+--- a/hggit/bundle.py
++++ b/hggit/bundle.py
+@@ -17,8 +17,8 @@ BUNDLEPART_MAP = b'exp-hg-git-map'
+ BUNDLEPART_TAGS = b'exp-hg-git-tags'
+ 
+ 
[email protected](bundle2, 'getrepocaps')
+-def getrepocaps(orig, repo, **kwargs):
[email protected](compat.bundle_caps_mod, compat.get_repo_caps_function_name)
++def get_repo_caps(orig, repo, **kwargs):
+     caps = orig(repo, **kwargs)
+ 
+     if repo.ui.configbool(b'experimental', b'hg-git-serve'):
+diff --git a/hggit/compat.py b/hggit/compat.py
+index a625ef16..a3ba9617 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -73,3 +73,14 @@ else:
+     from mercurial.commands import init as commands_init
+ 
+     assert commands_init  # silence pyflakes
++
++try:
++    # hg >= 7.2
++    from mercurial.exchanges import bundle_caps as bundle_caps_mod
++
++    get_repo_caps_function_name = 'get_repo_caps'
++except ImportError:
++    import mercurial.bundle2 as bundle_caps_mod
++
++    get_repo_caps_function_name = 'getrepocaps'
++    assert bundle_caps_mod  # silence pyflakes
diff --git a/debian/patches/compat-add-support-for-dulwich-0.23.2.patch b/debian/patches/compat-add-support-for-dulwich-0.23.2.patch
new file mode 100644
index 00000000..42a176b8
--- /dev/null
+++ b/debian/patches/compat-add-support-for-dulwich-0.23.2.patch
@@ -0,0 +1,46 @@
+From ed2cd8c4205c01c257810aa01c4b17777b0e133c Mon Sep 17 00:00:00 2001
+From: Dan Villiom Podlaski Christiansen <[email protected]>
+Date: Tue, 8 Jul 2025 17:53:27 +0200
+Subject: [PATCH] compat: add support for dulwich 0.23.2
+
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+new file mode 100644
+index 00000000..8106212d
+--- /dev/null
++++ b/hggit/compat.py
+@@ -0,0 +1,12 @@
++from dulwich.object_store import PackBasedObjectStore
++
++
++if hasattr(PackBasedObjectStore, 'delete_loose_object'):
++
++    def delete_loose_object(object_store: PackBasedObjectStore, sha: bytes):
++        object_store.delete_loose_object(sha)
++
++else:
++
++    def delete_loose_object(object_store: PackBasedObjectStore, sha: bytes):
++        object_store._remove_loose_object(sha)
+diff --git a/hggit/gc.py b/hggit/gc.py
+index d1fd31ac..530a373b 100644
+--- a/hggit/gc.py
++++ b/hggit/gc.py
+@@ -8,6 +8,8 @@ import typing
+ from dulwich.object_store import PackBasedObjectStore
+ from mercurial import ui as uimod
+ 
++from . import compat
++
+ 
+ class Worker(threading.Thread):
+     """Worker thread that we can stop.
+@@ -56,7 +58,7 @@ def _process_batch(ui, object_store, shas):
+     object_store.add_objects(list(objects))
+ 
+     for obj, path in objects:
+-        object_store._remove_loose_object(obj.id)
++        compat.delete_loose_object(object_store, obj.id)
+ 
+     ui.debug(b'packed %d loose objects!\n' % len(shas))
+ 
diff --git a/debian/patches/compat-fix-test-url-parsing.py.patch b/debian/patches/compat-fix-test-url-parsing.py.patch
new file mode 100644
index 00000000..702a8507
--- /dev/null
+++ b/debian/patches/compat-fix-test-url-parsing.py.patch
@@ -0,0 +1,85 @@
+From 718c72efb61ffea4a53628ab9e45a5a18516c174 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Wed, 13 May 2026 13:15:02 +0200
+Subject: [PATCH] compat: fix test-url-parsing.py
+
+Before this commit, test-url-parsing.py was failing with the following error:
+
+```
+$ tests/run-tests.py --with-hg=<BASE>/venv3.14-hg721-dulwich025/bin/hg tests/test-url-parsing.py
+
+Traceback (most recent call last):
+  File "<BASE>/tests/test-url-parsing.py", line 113, in <module>
+    tc.setUp()
+    ~~~~~~~~^^
+  File "<BASE>/tests/test-url-parsing.py", line 21, in setUp
+    commands.init(ui.ui(), self.tmpdir)
+    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
+TypeError: init() takes 0 positional arguments but 2 were given
+
+ERROR: test-url-parsing.py output changed
+!
+Failed test-url-parsing.py: output changed and returned error code 1
+```
+
+The change was introduced in mercurial commit 43a67798b33a ("init: introduce a
+new initialization system to explicitly load some module").
+
+TODO:
+- we could move the compat code in the test, since it is used only there
+- we could use hasattr(mercurial.commands, "_init") instead of inspect
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+index aa4e6da5..a625ef16 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -1,3 +1,6 @@
++import inspect
++
++import mercurial.commands  # TODO: do not re-export mercurial.commands (use "as _commands"?)
+ from dulwich.object_store import PackBasedObjectStore
+ 
+ 
+@@ -57,3 +60,16 @@ except ImportError:
+     from mercurial.hg import defaultdest as default_dest
+ 
+     assert default_dest  # silence pyflakes
++
++if inspect.getfullargspec(mercurial.commands.init).args == []:
++    # hg >= 7.2
++    #
++    # this accesses the private function _init(), and assumes that:
++    #
++    # >>> inspect.getfullargspec(mercurial.commands._init)
++    # FullArgSpec(args=['ui', 'dest'], varargs=None, varkw='opts', defaults=(b'.',), kwonlyargs=[], kwonlydefaults=None, annotations={})
++    from mercurial.commands import _init as commands_init
++else:
++    from mercurial.commands import init as commands_init
++
++    assert commands_init  # silence pyflakes
+diff --git a/tests/test-url-parsing.py b/tests/test-url-parsing.py
+index b69f9eff..376271d0 100755
+--- a/tests/test-url-parsing.py
++++ b/tests/test-url-parsing.py
+@@ -7,10 +7,11 @@ except ImportError:
+     sys.exit(80)
+ 
+ import os, tempfile, unittest, shutil
+-from mercurial import ui, hg, commands, pycompat
++from mercurial import ui, hg, pycompat
+ 
+ sys.path.append(os.path.join(os.path.dirname(__file__), os.path.pardir))
+ 
++from hggit import compat
+ from hggit.git_handler import GitHandler
+ 
+ 
+@@ -18,7 +19,7 @@ class TestUrlParsing(object):
+     def setUp(self):
+         # create a test repo location.
+         self.tmpdir = tempfile.mkdtemp('hg-git_url-test').encode('utf-8')
+-        commands.init(ui.ui(), self.tmpdir)
++        compat.commands_init(ui.ui(), self.tmpdir)
+         repo = hg.repository(ui.ui(), self.tmpdir)
+         self.handler = GitHandler(repo, ui.ui())
+ 
diff --git a/debian/patches/compat-hg-7.2-moved-parthandler-from-bundle2-to-bundle2_part.patch b/debian/patches/compat-hg-7.2-moved-parthandler-from-bundle2-to-bundle2_part.patch
new file mode 100644
index 00000000..63308f3b
--- /dev/null
+++ b/debian/patches/compat-hg-7.2-moved-parthandler-from-bundle2-to-bundle2_part.patch
@@ -0,0 +1,56 @@
+From adf77111b5e3feef27f61204858ec88df563a108 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Sat, 2 May 2026 17:05:59 +0200
+Subject: [PATCH] compat: hg 7.2 moved parthandler from bundle2 to
+ bundle2_part_handlers
+
+The change happened on 2025-08-07 in mercurial 503d63ff9b1a ("cycle-breaking:
+register most bundle2 part handler in their own modules"), and was integrated in
+mercurial since 7.2.
+
+diff --git a/hggit/bundle.py b/hggit/bundle.py
+index 7d5bdd75..fc8d0392 100644
+--- a/hggit/bundle.py
++++ b/hggit/bundle.py
+@@ -6,6 +6,7 @@ from mercurial import exthelper
+ 
+ from mercurial.node import bin, hex
+ 
++from . import compat
+ 
+ eh = exthelper.exthelper()
+ 
+@@ -73,7 +74,7 @@ def _addpartsfromopts(orig, ui, repo, bundler, source, outgoing, opts):
+ 
+ @eh.extsetup
+ def install_server_support(ui):
+-    @bundle2.parthandler(BUNDLEPART_MAP)
++    @compat.parthandler(BUNDLEPART_MAP)
+     def handlebundlemap(op, inpart):
+         ui.debug(b'unbundling git map\n')
+ 
+@@ -105,7 +106,7 @@ def install_server_support(ui):
+ 
+         addpartrevgitmap(repo, bundler, outgoing)
+ 
+-    @bundle2.parthandler(BUNDLEPART_TAGS)
++    @compat.parthandler(BUNDLEPART_TAGS)
+     def handlebundletags(op, inpart):
+         with io.BytesIO() as buf:
+             while not inpart.consumed:
+diff --git a/hggit/compat.py b/hggit/compat.py
+index 8ca4e727..a0072f5e 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -20,3 +20,11 @@ except ImportError:
+     from dulwich.refs import PEELED_TAG_SUFFIX
+ 
+     assert PEELED_TAG_SUFFIX  # silence pyflakes
++
++try:
++    # hg >= 7.2
++    from mercurial.bundle2_part_handlers import parthandler
++except ImportError:
++    from mercurial.bundle2 import parthandler
++
++    assert parthandler  # silence pyflakes
diff --git a/debian/patches/compat-hg-7.2-removed-repository.peer-and-introduced-exchang.patch b/debian/patches/compat-hg-7.2-removed-repository.peer-and-introduced-exchang.patch
new file mode 100644
index 00000000..ec84749b
--- /dev/null
+++ b/debian/patches/compat-hg-7.2-removed-repository.peer-and-introduced-exchang.patch
@@ -0,0 +1,89 @@
+From 8988531739f832a933db8ef1efcbbebe4a39780d Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Wed, 13 May 2026 00:59:38 +0200
+Subject: [PATCH] compat: hg 7.2 removed repository.peer and introduced
+ exchanges.peer.Peer
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The change happened on 2025-10-16 in 15e04d999f43 ("interface: extract the
+non-typing code for the peer"), and was integrated in mercurial since 7.2.
+
+
+exchanges.peer.Peer is still an abstract class, but we can add trivial
+implementations for clonebundles() and get_cached_bundle_inline().
+
+The two concrete classes in hg >=7.2 are:
+1. mercurial.localrepo.localpeer
+2. mercurial.wireprotov1peer.wirepeer
+
+They will be used later in the series.
+
+Albeit not exact, we give the old "peer" class the new name "Peer", in order to
+smooth the change when we will get rid of the compatibility code.
+
+Co-authored-by: Jordi GutiĆ©rrez Hermoso <[email protected]>
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+index a0072f5e..cedfc5ed 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -28,3 +28,11 @@ except ImportError:
+     from mercurial.bundle2 import parthandler
+ 
+     assert parthandler  # silence pyflakes
++
++try:
++    # hg >= 7.2
++    from mercurial.exchanges.peer import Peer
++except ImportError:
++    from mercurial.interfaces.repository import peer as Peer
++
++    assert Peer  # silence pyflakes
+diff --git a/hggit/gitrepo.py b/hggit/gitrepo.py
+index dab22ce4..990eb300 100644
+--- a/hggit/gitrepo.py
++++ b/hggit/gitrepo.py
+@@ -9,15 +9,17 @@ from mercurial import (
+     pycompat,
+     wireprotov1peer,
+ )
+-from mercurial.interfaces import repository
+ from mercurial.utils import urlutil
+ 
+-from . import util
++from . import (
++    compat,
++    util,
++)
+ 
+ eh = exthelper.exthelper()
+ 
+ 
+-class gitrepo(repository.peer):
++class gitrepo(compat.Peer):
+     def __init__(self, ui, path=None, create=False, intents=None, **kwargs):
+         if create:  # pragma: no cover
+             raise error.Abort(b'Cannot create a git repository.')
+@@ -121,6 +123,20 @@ class gitrepo(repository.peer):
+     def _submitone(self, op, args):
+         return None
+ 
++    def clonebundles(self):
++        """Trivial implementation of abstract hg >= 7.2 method.
++
++        No harm for hg <= 7.1.2
++        """
++        pass
++
++    def get_cached_bundle_inline(self, path):
++        """Trivial implementation of abstract hg >= 7.2 method.
++
++        No harm for hg <= 7.1.2
++        """
++        pass
++
+ 
+ instance = gitrepo
+ 
diff --git a/debian/patches/compat-support-dulwich-0.25.patch b/debian/patches/compat-support-dulwich-0.25.patch
new file mode 100644
index 00000000..e6dc0a4e
--- /dev/null
+++ b/debian/patches/compat-support-dulwich-0.25.patch
@@ -0,0 +1,122 @@
+From 7a0fed39bebc3fe0e50dad69ee5ae6bcf44c4534 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Fri, 19 Dec 2025 23:03:17 +0100
+Subject: [PATCH] compat: support dulwich 0.25
+
+In dulwich 0.24.10 ANNOTATED_TAG_SUFFIX was already an alias for
+PEELED_TAG_SUFFIX. We stopped relying on the alias in the previous commit.
+
+Dulwich 0.25.0 removed the alias, and moved PEELED_TAG_SUFFIX from dulwich.refs
+to dulwich.protocol.
+
+This commit centralizes in hggit.compat a backward compatiblity fix for
+supporting PEELED_TAG_SUFFIX in dulwich <=0.24.10 and >=0.25.10.
+
+The tests that broke on dulwich 0.25.0 and that are now fixed are
+test-url-parsing.py and test-doctest.py.
+
+The reason for not importing compat.PEELED_TAG_SUFFIX is explained at
+https://foss.heptapod.net/mercurial/hg-git/-/merge_requests/269#note_512928
+
+> Normally, in Mercurial and hg-git, we never import attributes from a module,
+> we only ever import modules. This ensures that lazy importing works.
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+index 8106212d..8ca4e727 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -10,3 +10,13 @@ else:
+ 
+     def delete_loose_object(object_store: PackBasedObjectStore, sha: bytes):
+         object_store._remove_loose_object(sha)
++
++
++try:
++    # dulwich >= 0.25.0
++    from dulwich.protocol import PEELED_TAG_SUFFIX
++except ImportError:
++    # dulwich <= 0.24.10
++    from dulwich.refs import PEELED_TAG_SUFFIX
++
++    assert PEELED_TAG_SUFFIX  # silence pyflakes
+diff --git a/hggit/git2hg.py b/hggit/git2hg.py
+index 7b2dcad0..a83c3437 100644
+--- a/hggit/git2hg.py
++++ b/hggit/git2hg.py
+@@ -8,7 +8,6 @@ from dulwich.objects import Commit, Tag
+ from dulwich.refs import (
+     LOCAL_BRANCH_PREFIX,
+     LOCAL_TAG_PREFIX,
+-    PEELED_TAG_SUFFIX,
+ )
+ from mercurial.i18n import _
+ 
+@@ -16,6 +15,7 @@ from mercurial.node import bin, short
+ from mercurial import error, util as hgutil
+ from mercurial import phases
+ 
++from . import compat
+ from . import config
+ 
+ 
+@@ -306,7 +306,7 @@ def filter_refs(refs, heads):
+                     raise error.RepoLookupError(msg)
+     else:
+         for ref, sha in refs.items():
+-            if not ref.endswith(PEELED_TAG_SUFFIX) and (
++            if not ref.endswith(compat.PEELED_TAG_SUFFIX) and (
+                 ref.startswith(LOCAL_BRANCH_PREFIX)
+                 or ref.startswith(LOCAL_TAG_PREFIX)
+                 or ref == b'HEAD'
+diff --git a/hggit/git_handler.py b/hggit/git_handler.py
+index 59e19673..458c6c34 100644
+--- a/hggit/git_handler.py
++++ b/hggit/git_handler.py
+@@ -13,7 +13,6 @@ from dulwich.pack import apply_delta
+ from dulwich.refs import (
+     LOCAL_BRANCH_PREFIX,
+     LOCAL_TAG_PREFIX,
+-    PEELED_TAG_SUFFIX,
+ )
+ from dulwich.repo import Repo, check_ref_format
+ from dulwich import client
+@@ -40,6 +39,7 @@ from mercurial import (
+ )
+ 
+ from . import _ssh
++from . import compat
+ from . import gc
+ from . import git2hg
+ from . import hg2git
+@@ -1640,8 +1640,11 @@ class GitHandler(object):
+             # pull tags pointing to known revisions, including
+             # annotated tags
+             for ref, sha in refs.items():
+-                if ref.endswith(PEELED_TAG_SUFFIX) and sha in self._map_git:
+-                    actual_ref = ref[: -len(PEELED_TAG_SUFFIX)]
++                if (
++                    ref.endswith(compat.PEELED_TAG_SUFFIX)
++                    and sha in self._map_git
++                ):
++                    actual_ref = ref[: -len(compat.PEELED_TAG_SUFFIX)]
+                     filteredrefs.setdefault(actual_ref, refs[actual_ref])
+ 
+             return [x for x in filteredrefs.values() if x not in self.git]
+@@ -1857,7 +1860,7 @@ class GitHandler(object):
+         repotags = self.repo.tags()
+         new_refs = {}
+         for k in refs:
+-            if k.endswith(PEELED_TAG_SUFFIX) or not k.startswith(
++            if k.endswith(compat.PEELED_TAG_SUFFIX) or not k.startswith(
+                 LOCAL_TAG_PREFIX
+             ):
+                 continue
+@@ -2038,7 +2041,7 @@ class GitHandler(object):
+ 
+         for ref_name, sha in refs.items():
+             if (
+-                ref_name.endswith(PEELED_TAG_SUFFIX)
++                ref_name.endswith(compat.PEELED_TAG_SUFFIX)
+                 or sha not in self.git.object_store
+             ):
+                 # the sha points to a peeled tag; we should either
diff --git a/debian/patches/compat-support-dulwich-1.2.x.patch b/debian/patches/compat-support-dulwich-1.2.x.patch
new file mode 100644
index 00000000..f1d64d85
--- /dev/null
+++ b/debian/patches/compat-support-dulwich-1.2.x.patch
@@ -0,0 +1,43 @@
+From 4b907287d35c1c828aeeac5ba0270cb5be9b8786 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Wed, 20 May 2026 10:04:08 +0200
+Subject: [PATCH] compat: support dulwich 1.2.x
+
+Dulwich deprecated Commit.extra in 0.21.3, and removed it in 1.0.0. The relevant
+file in Dulwich is dulwich/objects.py.
+
+The private member _extra has been present since before our earliest supported
+version, and it is still present in Dulwich 1.2.x.
+
+To keep compatibility with all Dulwich versions (albeit in a fragile way), we
+will rely on _extra, since the construct is only used in test-extra.t and not in
+any other hg-git code.
+
+Apart from this commit, no other changes are needed to support Dulwich 1.2.x
+(and 1.0.0 before that).
+
+diff --git a/tests/test-extra.t b/tests/test-extra.t
+index 23d4637c..5bc7836c 100644
+--- a/tests/test-extra.t
++++ b/tests/test-extra.t
+@@ -158,9 +158,19 @@ lets you do that, though.
+   >>> c.parents = [b'$commit_sha']
+   >>> c.tree = b'$tree_sha'
+   >>> c.message = b'extra commit\n'
+-  >>> c.extra.extend([(b'zzz:zzz', b'data:zzz'), (b'aaa:aaa', b'data:aaa'),
++  >>> c._extra.extend([(b'zzz:zzz', b'data:zzz'), (b'aaa:aaa', b'data:aaa'),
+   ...                 (b'HG:extra', b'hgaaa:dataaaa'),
+   ...                 (b'HG:extra', b'hgzzz:datazzz')])
++  ... # In the above line, please note that Dulwich deprecated Commit.extra in
++  ... # 0.21.3, and removed it in 1.0.0 (the relevant file there is
++  ... # dulwich/objects.py).
++  ... #
++  ... # The private member _extra has been present since before our earliest
++  ... # supported version, and it is still present in Dulwich 1.2.x.
++  ... #
++  ... # To keep compatibility with all Dulwich versions (albeit in a fragile
++  ... # way), we will rely on _extra, since the construct is only used in this
++  ... # test and not in any other hg-git code.
+   >>> repo.object_store.add_object(c)
+   >>> repo.refs.set_if_equals(b'refs/heads/master', None, c.id)
+   True
diff --git a/debian/patches/compat-use-PEELED_TAG_SUFFIX-instead-of-ANNOTATED_TAG_SUFFIX.patch b/debian/patches/compat-use-PEELED_TAG_SUFFIX-instead-of-ANNOTATED_TAG_SUFFIX.patch
new file mode 100644
index 00000000..0572a1bc
--- /dev/null
+++ b/debian/patches/compat-use-PEELED_TAG_SUFFIX-instead-of-ANNOTATED_TAG_SUFFIX.patch
@@ -0,0 +1,88 @@
+From d5cba79c7ba00ca024da4482da2eef988d6f49a9 Mon Sep 17 00:00:00 2001
+From: Antonio Muci <[email protected]>
+Date: Fri, 19 Dec 2025 23:14:44 +0100
+Subject: [PATCH] compat: use PEELED_TAG_SUFFIX instead of ANNOTATED_TAG_SUFFIX
+
+Starting from our oldest supported dulwich (0.22.1), and until 0.24.10,
+ANNOTATED_TAG_SUFFIX has been an alias for PEELED_TAG_SUFFIX (see sources later
+on).
+
+Dulwich 0.25.0 is going to remove support for the alias and move
+PEELED_TAG_SUFFIX in another module. Let's address the aliasing in this first
+commit. The next one will add compatibility with both old and recent dulwich
+(>= 0.25.0).
+
+No functional changes.
+
+Source:
+- https://github.com/jelmer/dulwich/blob/dulwich-0.22.1/dulwich/refs.py#L45
+- https://github.com/jelmer/dulwich/blob/dulwich-0.24.10/dulwich/refs.py#L62
+
+diff --git a/hggit/git2hg.py b/hggit/git2hg.py
+index 6f19c8af..7b2dcad0 100644
+--- a/hggit/git2hg.py
++++ b/hggit/git2hg.py
+@@ -6,9 +6,9 @@ import itertools
+ from dulwich import config as dul_config
+ from dulwich.objects import Commit, Tag
+ from dulwich.refs import (
+-    ANNOTATED_TAG_SUFFIX,
+     LOCAL_BRANCH_PREFIX,
+     LOCAL_TAG_PREFIX,
++    PEELED_TAG_SUFFIX,
+ )
+ from mercurial.i18n import _
+ 
+@@ -306,7 +306,7 @@ def filter_refs(refs, heads):
+                     raise error.RepoLookupError(msg)
+     else:
+         for ref, sha in refs.items():
+-            if not ref.endswith(ANNOTATED_TAG_SUFFIX) and (
++            if not ref.endswith(PEELED_TAG_SUFFIX) and (
+                 ref.startswith(LOCAL_BRANCH_PREFIX)
+                 or ref.startswith(LOCAL_TAG_PREFIX)
+                 or ref == b'HEAD'
+diff --git a/hggit/git_handler.py b/hggit/git_handler.py
+index 73457cdb..59e19673 100644
+--- a/hggit/git_handler.py
++++ b/hggit/git_handler.py
+@@ -11,9 +11,9 @@ from dulwich.errors import HangupException, GitProtocolError, ApplyDeltaError
+ from dulwich.objects import Blob, Commit, Tag, Tree, parse_timezone
+ from dulwich.pack import apply_delta
+ from dulwich.refs import (
+-    ANNOTATED_TAG_SUFFIX,
+     LOCAL_BRANCH_PREFIX,
+     LOCAL_TAG_PREFIX,
++    PEELED_TAG_SUFFIX,
+ )
+ from dulwich.repo import Repo, check_ref_format
+ from dulwich import client
+@@ -1640,8 +1640,8 @@ class GitHandler(object):
+             # pull tags pointing to known revisions, including
+             # annotated tags
+             for ref, sha in refs.items():
+-                if ref.endswith(ANNOTATED_TAG_SUFFIX) and sha in self._map_git:
+-                    actual_ref = ref[: -len(ANNOTATED_TAG_SUFFIX)]
++                if ref.endswith(PEELED_TAG_SUFFIX) and sha in self._map_git:
++                    actual_ref = ref[: -len(PEELED_TAG_SUFFIX)]
+                     filteredrefs.setdefault(actual_ref, refs[actual_ref])
+ 
+             return [x for x in filteredrefs.values() if x not in self.git]
+@@ -1857,7 +1857,7 @@ class GitHandler(object):
+         repotags = self.repo.tags()
+         new_refs = {}
+         for k in refs:
+-            if k.endswith(ANNOTATED_TAG_SUFFIX) or not k.startswith(
++            if k.endswith(PEELED_TAG_SUFFIX) or not k.startswith(
+                 LOCAL_TAG_PREFIX
+             ):
+                 continue
+@@ -2038,7 +2038,7 @@ class GitHandler(object):
+ 
+         for ref_name, sha in refs.items():
+             if (
+-                ref_name.endswith(ANNOTATED_TAG_SUFFIX)
++                ref_name.endswith(PEELED_TAG_SUFFIX)
+                 or sha not in self.git.object_store
+             ):
+                 # the sha points to a peeled tag; we should either
diff --git a/debian/patches/compat-wrap-correct-functions-after-hg-7.2-moved-them.patch b/debian/patches/compat-wrap-correct-functions-after-hg-7.2-moved-them.patch
new file mode 100644
index 00000000..105ec9e1
--- /dev/null
+++ b/debian/patches/compat-wrap-correct-functions-after-hg-7.2-moved-them.patch
@@ -0,0 +1,84 @@
+From cc438f2038bfa4d51e05752e56c07221b94c2fb2 Mon Sep 17 00:00:00 2001
+From: Manuel Jacob <[email protected]>
+Date: Thu, 20 Aug 2026 17:20:07 +0200
+Subject: [PATCH] compat: wrap correct functions after hg 7.2 moved them
+
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+index a3ba9617..abc6ca2f 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -1,3 +1,4 @@
++import importlib
+ import inspect
+ 
+ import mercurial.commands  # TODO: do not re-export mercurial.commands (use "as _commands"?)
+@@ -84,3 +85,19 @@ except ImportError:
+ 
+     get_repo_caps_function_name = 'getrepocaps'
+     assert bundle_caps_mod  # silence pyflakes
++
++
++def wrap_first_function(eh, function_refs):
++    for function_ref in function_refs:
++        module_name, function_name = function_ref.split(':')
++        try:
++            module = importlib.import_module(module_name)
++        except ImportError:
++            continue
++        try:
++            getattr(module, function_name)
++        except AttributeError:
++            continue
++        return eh.wrapfunction(module, function_name)
++    else:
++        assert False
+diff --git a/hggit/schemes.py b/hggit/schemes.py
+index 5b40eab0..4953e4b2 100644
+--- a/hggit/schemes.py
++++ b/hggit/schemes.py
+@@ -103,7 +103,13 @@ def default_dest(orig, source):
+     return orig(source)
+ 
+ 
[email protected](hg, 'peer')
[email protected]_first_function(
++    eh,
++    [
++        'mercurial.repo.factory:peer',  # hg >= 7.2
++        'mercurial.hg:peer',
++    ],
++)
+ def peer(orig, uiorrepo, *args, **opts):
+     newpeer = orig(uiorrepo, *args, **opts)
+     if isinstance(newpeer, gitrepo.gitrepo):
+@@ -112,7 +118,13 @@ def peer(orig, uiorrepo, *args, **opts):
+     return newpeer
+ 
+ 
[email protected](hg, 'clone')
[email protected]_first_function(
++    eh,
++    [
++        'mercurial.cmd_impls.clone:clone',  # hg >= 7.2
++        'mercurial.hg:clone',
++    ],
++)
+ def clone(orig, *args, **opts):
+     srcpeer, destpeer = orig(*args, **opts)
+ 
+@@ -134,7 +146,13 @@ def isurllocal(orig, path):
+     return orig(path) and not util.isgitsshuri(path._origpath)
+ 
+ 
[email protected](hg, 'islocal')
[email protected]_first_function(
++    eh,
++    [
++        'mercurial.repo.factory:is_local',  # hg >= 7.2
++        'mercurial.hg:islocal',
++    ],
++)
+ def islocal(orig, path):
+     # recognise git scp-style paths when cloning
+     return orig(path) and not util.isgitsshuri(path)
diff --git a/debian/patches/dulwich-checksum-mismatch b/debian/patches/dulwich-checksum-mismatch
new file mode 100644
index 00000000..cc6a50c3
--- /dev/null
+++ b/debian/patches/dulwich-checksum-mismatch
@@ -0,0 +1,40 @@
+Description: Handle ChecksumMismatch raised by dulwich >= 1.2.7 in gverify --fsck
+Forwarded: no
+Index: hg-git/hggit/commands.py
+===================================================================
+--- hg-git.orig/hggit/commands.py
++++ hg-git/hggit/commands.py
+@@ -10,6 +10,7 @@
+ import os
+ 
+ from dulwich import porcelain
++from dulwich.errors import ChecksumMismatch
+ 
+ from mercurial.node import hex, nullhex
+ from mercurial.i18n import _
+@@ -83,7 +84,10 @@ def gverify(ui, repo, **opts):
+     '''
+ 
+     if opts.get('fsck'):
+-        for badsha, e in porcelain.fsck(repo.githandler.git):
++        try:
++            for badsha, e in porcelain.fsck(repo.githandler.git):
++                raise error.Abort(b'git repository is corrupt!')
++        except ChecksumMismatch:
+             raise error.Abort(b'git repository is corrupt!')
+ 
+     ctx = scmutil.revsingle(repo, opts.get('rev'), b'.')
+Index: hg-git/tests/test-verify-fail.t
+===================================================================
+--- hg-git.orig/tests/test-verify-fail.t
++++ hg-git/tests/test-verify-fail.t
+@@ -84,7 +84,8 @@ corrupt git repository
+   abort: git repository is corrupt!
+   [255]
+   $ hg gverify
+-  abort: git equivalent 6dff77b710b6f0961ac0b6d91d85902195133d74 for rev 5c2eb98af3e2 is not a commit!
++  abort: git equivalent 6dff77b710b6f0961ac0b6d91d85902195133d74 for rev 5c2eb98af3e2 is corrupt!
++  (re-run with --traceback for details)
+   [255]
+   $ chmod +w .hg/git/objects/6d/ff77b710b6f0961ac0b6d91d85902195133d74
+   $ echo 42 > .hg/git/objects/6d/ff77b710b6f0961ac0b6d91d85902195133d74
diff --git a/debian/patches/dulwich-compat b/debian/patches/dulwich-compat
index 6979feff..75c7e6ea 100644
--- a/debian/patches/dulwich-compat
+++ b/debian/patches/dulwich-compat
@@ -1,37 +1,3 @@
-Index: hg-git/hggit/gc.py
-===================================================================
---- hg-git.orig/hggit/gc.py
-+++ hg-git/hggit/gc.py
-@@ -56,7 +56,7 @@ def _process_batch(ui, object_store, sha
-     object_store.add_objects(list(objects))
- 
-     for obj, path in objects:
--        object_store._remove_loose_object(obj.id)
-+        object_store.delete_loose_object(obj.id)
- 
-     ui.debug(b'packed %d loose objects!\n' % len(shas))
- 
-Index: hg-git/hggit/git_handler.py
-===================================================================
---- hg-git.orig/hggit/git_handler.py
-+++ hg-git/hggit/git_handler.py
-@@ -2127,8 +2127,14 @@ class GitHandler(object):
-         )
- 
-         for change in changes:
--            oldfile, oldmode, oldsha = change.old
--            newfile, newmode, newsha = change.new
-+            if change.old is not None:
-+                oldfile, oldmode, oldsha = change.old
-+            else:
-+                oldfile, oldmode, oldsha = None, None, None
-+            if change.new is not None:
-+                newfile, newmode, newsha = change.new
-+            else:
-+                newfile, newmode, newsha = None, None, None
-             # actions are described by the following table ('no' means 'does
-             # not exist'):
-             #    old        new     |    action
 Index: hg-git/setup.cfg
 ===================================================================
 --- hg-git.orig/setup.cfg
@@ -41,7 +7,7 @@ Index: hg-git/setup.cfg
      hggit.helptext
  install_requires=
 -    dulwich>=0.21.6,<0.23.0
-+    dulwich>=0.24.0,<0.25
++    dulwich>=0.24.0,<2.0.0
  
  [options.package_data]
  * = *.txt, *.rst
diff --git a/debian/patches/git_handler-fix-a-compatibility-issue-with-dulwich-0.24.0.patch b/debian/patches/git_handler-fix-a-compatibility-issue-with-dulwich-0.24.0.patch
new file mode 100644
index 00000000..ab1ec8b2
--- /dev/null
+++ b/debian/patches/git_handler-fix-a-compatibility-issue-with-dulwich-0.24.0.patch
@@ -0,0 +1,35 @@
+From e295ae14f049a1cf560147b1de0538d69669a24a Mon Sep 17 00:00:00 2001
+From: reconzo-abhijith <[email protected]>
+Date: Mon, 24 Nov 2025 14:44:46 +0530
+Subject: [PATCH] git_handler: fix a compatibility issue with dulwich >= 0.24.0
+
+This fixes errors when a pull operation involves new or deleted files.
+
+The error was:
+    TypeError: cannot unpack non-iterable NoneType object
+in git_handler.py, get_files_changed().
+
+Original proposal by @kebianizao and @muxator at
+https://foss.heptapod.net/mercurial/hg-git/-/issues/451
+
+Fixes #451.
+
+diff --git a/hggit/git_handler.py b/hggit/git_handler.py
+index 30f0fae3..2d6f6624 100644
+--- a/hggit/git_handler.py
++++ b/hggit/git_handler.py
+@@ -2131,8 +2131,12 @@ class GitHandler(object):
+         )
+ 
+         for change in changes:
+-            oldfile, oldmode, oldsha = change.old
+-            newfile, newmode, newsha = change.new
++            oldfile, oldmode, oldsha = (None, None, None)
++            newfile, newmode, newsha = (None, None, None)
++            if change.old:
++                oldfile, oldmode, oldsha = change.old
++            if change.new:
++                newfile, newmode, newsha = change.new
+             # actions are described by the following table ('no' means 'does
+             # not exist'):
+             #    old        new     |    action
diff --git a/debian/patches/git_handler-fix-a-dulwich-0.24.3-incompatibility-in-test-pus.patch b/debian/patches/git_handler-fix-a-dulwich-0.24.3-incompatibility-in-test-pus.patch
new file mode 100644
index 00000000..3fe125dc
--- /dev/null
+++ b/debian/patches/git_handler-fix-a-dulwich-0.24.3-incompatibility-in-test-pus.patch
@@ -0,0 +1,36 @@
+From 44103425cd23f330623fd2669e23a489777819b1 Mon Sep 17 00:00:00 2001
+From: muxator <[email protected]>
+Date: Wed, 26 Nov 2025 17:53:04 +0100
+Subject: [PATCH] git_handler: fix a dulwich 0.24.3 incompatibility in
+ test-push.t
+
+In Dulwich 0.24.3, the determine_wants() function started accepting an
+additional "depth" parameter.
+
+Without this change, test-push.t would break with Dulwich >=0.24.3.
+
+This change should be compatible with both the old and the new Dulwich version.
+
+Compare the following:
+- 0.24.2 (without "depth"): https://github.com/jelmer/dulwich/blob/dulwich-0.24.2/dulwich/repo.py#L590
+- 0.24.3 (with "depth"):    https://github.com/jelmer/dulwich/blob/dulwich-0.24.3/dulwich/repo.py#L601
+
+diff --git a/hggit/git_handler.py b/hggit/git_handler.py
+index 2d6f6624..6aee91bc 100644
+--- a/hggit/git_handler.py
++++ b/hggit/git_handler.py
+@@ -1622,7 +1622,13 @@ class GitHandler(object):
+         haveheads.extend(self.git.refs.as_dict(LOCAL_BRANCH_PREFIX).values())
+         graphwalker = self.git.get_graph_walker(heads=haveheads)
+ 
+-        def determine_wants(refs):
++        # Starting with dulwich 0.24.3, determine_wants() needs to accept an
++        # additional parameter "depth".
++        #
++        # Compare the following:
++        # - 0.24.2 (without "depth"): https://github.com/jelmer/dulwich/blob/dulwich-0.24.2/dulwich/repo.py#L590
++        # - 0.24.3 (with "depth")   : https://github.com/jelmer/dulwich/blob/dulwich-0.24.3/dulwich/repo.py#L601
++        def determine_wants(refs, depth: int | None = None):
+             if refs is None:
+                 return None
+             filteredrefs = git2hg.filter_refs(refs, heads)
diff --git a/debian/patches/git_handler-fix-a-dulwich-0.24.3-incompatibility.patch b/debian/patches/git_handler-fix-a-dulwich-0.24.3-incompatibility.patch
new file mode 100644
index 00000000..5b39e586
--- /dev/null
+++ b/debian/patches/git_handler-fix-a-dulwich-0.24.3-incompatibility.patch
@@ -0,0 +1,39 @@
+From da4efae81e1e5814a23c74a0e283d103f172cc4b Mon Sep 17 00:00:00 2001
+From: reconzo-abhijith <[email protected]>
+Date: Mon, 24 Nov 2025 14:44:46 +0530
+Subject: [PATCH] git_handler: fix a dulwich 0.24.3 incompatibility
+
+In Dulwich 0.24.3, get_credentials_from_store() started returning an Iterator of
+tuples instead of a single tuple. This change covers all the cases.
+
+see: https://foss.heptapod.net/mercurial/hg-git/-/merge_requests/260#note_493621
+
+Definition of get_credentials_from_store() in dulwich 0.24.2 (without Iterator):
+    https://github.com/jelmer/dulwich/blob/dulwich-0.24.2/dulwich/client.py#L3685
+Definition of get_credentials_from_store() in dulwich 0.24.3 (changed to Iterator):
+    https://github.com/jelmer/dulwich/blob/dulwich-0.24.3/dulwich/client.py#L4275
+
+diff --git a/hggit/git_handler.py b/hggit/git_handler.py
+index 6aee91bc..11ad2203 100644
+--- a/hggit/git_handler.py
++++ b/hggit/git_handler.py
+@@ -3,6 +3,7 @@ import itertools
+ import os
+ import re
+ import shutil
++import types
+ 
+ from dulwich.client import HTTPUnauthorized
+ from dulwich.errors import HangupException, GitProtocolError, ApplyDeltaError
+@@ -2396,6 +2397,11 @@ class GitHandler(object):
+                 urlobj.user,
+             )
+ 
++            # fix for dulwich >= 0.24.3 compatibility
++            # see: https://foss.heptapod.net/mercurial/hg-git/-/merge_requests/260#note_493621
++            if isinstance(auth, types.GeneratorType):
++                auth = next(auth, None)
++
+             if self._http_auth_realm:
+                 # since we've tried an unauthenticated request, and
+                 # obtain a realm, we can do a "full" search, including
diff --git a/debian/patches/gitrepo-add_branch_revs-was-moved-in-a-different-module-in-h.patch b/debian/patches/gitrepo-add_branch_revs-was-moved-in-a-different-module-in-h.patch
new file mode 100644
index 00000000..d090e223
--- /dev/null
+++ b/debian/patches/gitrepo-add_branch_revs-was-moved-in-a-different-module-in-h.patch
@@ -0,0 +1,55 @@
+From 6b74e944291e2a09518fe5cba6b966945c073d5a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jordi=20Guti=C3=A9rrez=20Hermoso?= <[email protected]>
+Date: Wed, 13 May 2026 01:12:24 +0200
+Subject: [PATCH] gitrepo: add_branch_revs() was moved in a different module in
+ hg >= 7.2
+
+The change happened in mercurial commit:
+33a40f393800 ("cycle-breaking: move `add_branch_revs` from `hg` to `urlutil`")
+
+Co-authored-by: Antonio Muci <[email protected]>
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+index cedfc5ed..8addccc1 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -36,3 +36,16 @@ except ImportError:
+     from mercurial.interfaces.repository import peer as Peer
+ 
+     assert Peer  # silence pyflakes
++
++import mercurial.utils.urlutil as _urlutil  # do not re-export urlutil
++
++if hasattr(_urlutil, 'add_branch_revs'):
++    # hg >= 7.2
++    from mercurial.utils import urlutil as add_branch_revs_mod
++
++    add_branch_revs_function_name = 'add_branch_revs'
++else:
++    from mercurial import hg as add_branch_revs_mod
++
++    add_branch_revs_function_name = 'addbranchrevs'
++    assert add_branch_revs_mod  # silence pyflakes
+diff --git a/hggit/gitrepo.py b/hggit/gitrepo.py
+index 990eb300..c988b117 100644
+--- a/hggit/gitrepo.py
++++ b/hggit/gitrepo.py
+@@ -5,7 +5,6 @@ from mercurial import (
+     error,
+     exchange,
+     exthelper,
+-    hg,
+     pycompat,
+     wireprotov1peer,
+ )
+@@ -150,7 +149,9 @@ def islocal(path):
+ 
+ 
+ # defend against tracebacks if we specify -r in 'hg pull'
[email protected](hg, 'addbranchrevs')
[email protected](
++    compat.add_branch_revs_mod, compat.add_branch_revs_function_name
++)
+ def safebranchrevs(orig, lrepo, otherrepo, branches, revs, **kwargs):
+     revs, co = orig(lrepo, otherrepo, branches, revs, **kwargs)
+     if isinstance(otherrepo, gitrepo):
diff --git a/debian/patches/gitrepo-default_dest-was-moved-in-a-different-module-in-hg-7.patch b/debian/patches/gitrepo-default_dest-was-moved-in-a-different-module-in-hg-7.patch
new file mode 100644
index 00000000..9647efc7
--- /dev/null
+++ b/debian/patches/gitrepo-default_dest-was-moved-in-a-different-module-in-hg-7.patch
@@ -0,0 +1,50 @@
+From 2e29e9b2a45914905caaa6ddbc03455f62c742b4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jordi=20Guti=C3=A9rrez=20Hermoso?= <[email protected]>
+Date: Wed, 13 May 2026 01:21:11 +0200
+Subject: [PATCH] gitrepo: default_dest() was moved in a different module in hg
+ >= 7.2
+
+The change happened in mercurial commit:
+a3b891ff921e ("cycle-breaking: move `default_dest` from `hg` to a `clone` module")
+
+Co-authored-by: Antonio Muci <[email protected]>
+
+diff --git a/hggit/compat.py b/hggit/compat.py
+index 8addccc1..aa4e6da5 100644
+--- a/hggit/compat.py
++++ b/hggit/compat.py
+@@ -49,3 +49,11 @@ else:
+ 
+     add_branch_revs_function_name = 'addbranchrevs'
+     assert add_branch_revs_mod  # silence pyflakes
++
++try:
++    # hg >= 7.2
++    from mercurial.cmd_impls.clone import default_dest
++except ImportError:
++    from mercurial.hg import defaultdest as default_dest
++
++    assert default_dest  # silence pyflakes
+diff --git a/hggit/schemes.py b/hggit/schemes.py
+index bd5f7e29..5b40eab0 100644
+--- a/hggit/schemes.py
++++ b/hggit/schemes.py
+@@ -18,6 +18,7 @@ from mercurial import (
+ from mercurial.utils import urlutil
+ 
+ # local modules
++from . import compat
+ from . import gitrepo
+ from . import util
+ 
+@@ -94,8 +95,8 @@ class RepoFactory:
+         return fn(ui, path, *args, **kwargs)
+ 
+ 
[email protected](hg, 'defaultdest')
+-def defaultdest(orig, source):
[email protected](compat, 'default_dest')
++def default_dest(orig, source):
+     if source.endswith(b'.git'):
+         return orig(source[:-4])
+ 
diff --git a/debian/patches/gitrepo-selectively-define-instance-function.patch b/debian/patches/gitrepo-selectively-define-instance-function.patch
new file mode 100644
index 00000000..d78ca710
--- /dev/null
+++ b/debian/patches/gitrepo-selectively-define-instance-function.patch
@@ -0,0 +1,29 @@
+From ea35d410148553d9ff633a4b0ba49a7e7c6465b5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jordi=20Guti=C3=A9rrez=20Hermoso?= <[email protected]>
+Date: Sat, 28 Mar 2026 16:44:43 -0600
+Subject: [PATCH] gitrepo: selectively define `instance` function
+
+Because of how Mercurial now instantiates repos, we can no longer be
+unconditionally a gitrepo. Instead, we return a localrepo for local
+creation.
+
+This is so that `hg clone some_git_repo` works.
+
+diff --git a/hggit/gitrepo.py b/hggit/gitrepo.py
+index c988b117..5b8ec595 100644
+--- a/hggit/gitrepo.py
++++ b/hggit/gitrepo.py
+@@ -137,7 +137,12 @@ class gitrepo(compat.Peer):
+         pass
+ 
+ 
+-instance = gitrepo
++def instance(ui, path, create=False, *args, **kwargs):
++    if create and islocal(path):
++        from mercurial import localrepo
++
++        return localrepo.instance(ui, path, create, *args, **kwargs)
++    return gitrepo(ui, path, create, *args, **kwargs)
+ 
+ 
+ def islocal(path):
diff --git a/debian/patches/hggit-fix-an-incompatibility-with-dulwich-0.24.0.patch b/debian/patches/hggit-fix-an-incompatibility-with-dulwich-0.24.0.patch
new file mode 100644
index 00000000..fac21e2e
--- /dev/null
+++ b/debian/patches/hggit-fix-an-incompatibility-with-dulwich-0.24.0.patch
@@ -0,0 +1,28 @@
+From 2971dfab4ed43653cb870818f126985266d4f2bb Mon Sep 17 00:00:00 2001
+From: Dan Villiom Podlaski Christiansen <[email protected]>
+Date: Tue, 25 Nov 2025 19:51:14 +0100
+Subject: [PATCH] hggit: fix an incompatibility with dulwich >= 0.24.0
+
+On dulwich >= 0.24, ssh commands arrive as undecoded bytes, and not strings.
+This would trigger an AssertionError when doing network operations.
+
+Modified form https://foss.heptapod.net/mercurial/hg-git/-/merge_requests/260/
+by Abhijith Nayak (@reconzo-abhijith).
+
+diff --git a/hggit/_ssh.py b/hggit/_ssh.py
+index d3bdc947..1ae55b2a 100644
+--- a/hggit/_ssh.py
++++ b/hggit/_ssh.py
+@@ -20,8 +20,10 @@ def generate_ssh_vendor(ui):
+         def run_command(
+             self, host, command, username=None, port=None, **kwargs
+         ):
+-            assert isinstance(command, str)
+-            command = command.encode(SSHGitClient.DEFAULT_ENCODING)
++            # on dulwich >= 0.24, command is bytes already
++            # on dulwich <= 0.23, command is a string
++            if isinstance(command, str):
++                command = command.encode(SSHGitClient.DEFAULT_ENCODING)
+             sshcmd = ui.config(b"ui", b"ssh", b"ssh")
+             args = procutil.sshargs(
+                 sshcmd, pycompat.bytesurl(host), username, port
diff --git a/debian/patches/series b/debian/patches/series
index b1b6fc68..f0529dde 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,19 @@
+compat-add-support-for-dulwich-0.23.2.patch
+hggit-fix-an-incompatibility-with-dulwich-0.24.0.patch
+git_handler-fix-a-compatibility-issue-with-dulwich-0.24.0.patch
+git_handler-fix-a-dulwich-0.24.3-incompatibility-in-test-pus.patch
+git_handler-fix-a-dulwich-0.24.3-incompatibility.patch
+compat-use-PEELED_TAG_SUFFIX-instead-of-ANNOTATED_TAG_SUFFIX.patch
+compat-support-dulwich-0.25.patch
+compat-support-dulwich-1.2.x.patch
+compat-hg-7.2-moved-parthandler-from-bundle2-to-bundle2_part.patch
+compat-hg-7.2-removed-repository.peer-and-introduced-exchang.patch
+gitrepo-add_branch_revs-was-moved-in-a-different-module-in-h.patch
+gitrepo-default_dest-was-moved-in-a-different-module-in-hg-7.patch
+gitrepo-selectively-define-instance-function.patch
+compat-fix-test-url-parsing.py.patch
+bundle-fix-test-bundle.t.-be-backward-compatible-in-a-ugly-w.patch
+bundle-fixe-test-hg-clone.t-and-test-serve-hg.t-with-hggit-o.patch
+compat-wrap-correct-functions-after-hg-7.2-moved-them.patch
 dulwich-compat
+dulwich-checksum-mismatch

Reply via email to