From c9f31b6c341457a802d7d1fd537ff3500d83164b Mon Sep 17 00:00:00 2001 From: Christopher Toth Date: Tue, 20 Oct 2026 13:50:47 -0600 Subject: [PATCH] Fix loose-object reads with Dulwich 1.2.10 --- .../fix-dulwich-loose-object-reading.md | 71 +++++++++++++++++ pyproject.toml | 2 +- tests/test_git_store.py | 21 +++++ uv.lock | 76 +++++++++---------- 4 files changed, 131 insertions(+), 39 deletions(-) create mode 100644 agent-notes/fix-dulwich-loose-object-reading.md diff --git a/agent-notes/fix-dulwich-loose-object-reading.md b/agent-notes/fix-dulwich-loose-object-reading.md new file mode 100644 index 0000000..bb4a717 --- /dev/null +++ b/agent-notes/fix-dulwich-loose-object-reading.md @@ -0,0 +1,71 @@ +# Dulwich loose-object reading + +## Goal +Independently reproduce Quire's exposure to Dulwich 1.2.9's loose-object parser +regression, then deliver the smallest dependency/test repair as a draft PR. +Leave PR #8 and Q's dirty checkout untouched. Parent owns review and merge. + +## State +- Original checkout: C:/Users/Q/code/quire, remote git@github.com:ctoth/quire.git; + GitHub resolves the transferred canonical repository to propstore/quire. +- Isolated branch: fix/dulwich-loose-object-reading, based on master 6845641. +- Read global/repository instructions and relevant revival/lease agent notes. + This repository has no populated .agents/skills instructions. +- Current main requires dulwich>=1.2.5 and locks 1.2.6. Open PR #8 raises both + to 1.2.9; only #7 (urllib3) and #8 are open, so no duplicate repair exists. +- Read upstream 1.2.9 objects.py and the 1.2.10 parser patch/tests. In 1.2.9, + unconsumed_tail is rejected before searching for the header's NUL terminator. + 1.2.10 bounds the header while accepting content beyond 8192 bytes. +- Repair implemented: minimum/lock 1.2.10; one parameterized regression verifies + a loose blob exists on disk, closes the original repository and reads it + through a newly opened GitStore. No production Python changes. + +## Actual validation +- Python 3.13.15, isolated installed development environment; shared .venv untouched. +- Installed Dulwich 1.2.9 with `uv pip install --python .venv/Scripts/python.exe + dulwich==1.2.9 --cache-dir ../uv-cache` solely for reproduction. +- `.venv/Scripts/python.exe -m pytest tests/test_git_store.py -k + reopened_disk_store_reads_blob_past_loose_header_window --basetemp + .pytest-dulwich-129 -q`: **2 failed, 1 passed, 49 deselected in 5.14s**. + 8182 bytes passed; 8183 and 200000 failed at read_file -> _walk_tree -> + _cached_object -> repo[object_id] -> DiskObjectStore._get_loose_object -> + ShaFile._parse_legacy_object_header (objects.py:509), raising + `zlib.error: object header exceeds maximum size`. +- Native `git cat-file -s` accepted the same on-disk blobs: + a3a69cb0b6b2ffd0c4f79f1ac3e9dc3645da8c4c = 8183 bytes; + a213aa40f11f7af2a9c9e973df599278413060bb = 200000 bytes. +- `uv sync --all-extras --dev --locked --cache-dir ../uv-cache` accepted the + repaired lock and installed actual Dulwich 1.2.10. +- `.venv/Scripts/python.exe -m pytest tests/test_git_store.py --basetemp + .pytest-dulwich-1210 -q`: **52 passed in 10.71s**. +- Actual 1.2.10 ShaFile parsing still rejects a 20000-digit header with + `object header exceeds maximum size`, and a 200000-byte valid blob with + max_size=1024 with `decompressed data exceeds maximum size`. +- Parsed-lock comparison: only Dulwich and Quire's Dulwich requirement metadata + differ; dependency edges and all other package records are identical. + All 36 locked release artifacts match official PyPI URL, SHA256, size and + timestamp (within lock millisecond precision). The existing 35 wheel entries + are retained; upstream cp313 Android tags move from API 21 to API 24 for both + arm64_v8a and x86_64. No claim that those two binary targets are identical. +- `git diff --check` passed. Full lint/typecheck/test/build matrix runs on the PR, + following the global instruction to use CI for full checks. + +## Decisions and why +Smallest expected repair: raise Dulwich's minimum to 1.2.10 (one manifest line), +replace only its locked artifact block and requirement metadata, and add one +parameterized public disk-store round-trip test (~20 lines). No parser override, +application changes, or unrelated dependency upgrades. Current task explicitly +authorizes this repair, regression tests and full relevant checks. + +## Surprises +Sandbox blocks network sockets by default; supported escalated GitHub reads and +fetch succeeded. No approval denial or uncertain mutation occurred. +- Initial artifact-verification probes wrongly expected exact timestamp string + precision and then every PyPI wheel to be locked. PyPI has microseconds and + additional targets beyond the existing lock. Corrected the comparison to + validate each retained artifact and report the actual target changes above; + no artifact hash/URL/size discrepancy or unrelated lock mutation was found. + +## Next step +Commit the four intended files, publish a draft, verify full CI and report the +exact head for parent's independent review. Do not merge or mark ready. diff --git a/pyproject.toml b/pyproject.toml index 0957afb..5d6d6c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "dulwich>=1.2.5", + "dulwich>=1.2.10", "msgspec>=0.19.0", "pyyaml>=6.0", ] diff --git a/tests/test_git_store.py b/tests/test_git_store.py index 537df8b..095f373 100644 --- a/tests/test_git_store.py +++ b/tests/test_git_store.py @@ -57,6 +57,27 @@ def test_commit_and_read_are_object_store_operations(tmp_path): assert not (root / "docs" / "example.yaml").exists() +@pytest.mark.parametrize("size", [8182, 8183, 200_000]) +def test_reopened_disk_store_reads_blob_past_loose_header_window(tmp_path, size): + # Dulwich 1.2.9 incorrectly applied its 8192-byte header limit to content. + # The 10-byte header plus 8182 bytes fits; 8183 bytes crosses that boundary. + root = tmp_path / "repo" + content = b"x" * size + store = GitStore.init(root) + try: + commit = store.commit_files({"docs/large.txt": content}, "store blob") + blob_id = Blob.from_string(content).id.decode("ascii") + assert (root / ".git" / "objects" / blob_id[:2] / blob_id[2:]).is_file() + finally: + store.raw_repo.close() + + reopened = GitStore.open(root) + try: + assert reopened.read_file("docs/large.txt", commit=commit) == content + finally: + reopened.raw_repo.close() + + def test_head_bound_transaction_captures_head_and_passes_expected_head(monkeypatch): store = GitStore.init_memory() first = store.commit_files({"seed.txt": b"seed"}, "seed") diff --git a/uv.lock b/uv.lock index 6076df4..85b258d 100644 --- a/uv.lock +++ b/uv.lock @@ -13,49 +13,49 @@ wheels = [ [[package]] name = "dulwich" -version = "1.2.6" +version = "1.2.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.12'" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/3d/7ea85d70d85f7d5ed5bf28dc742f106d8334e84286fbc852d983273dd890/dulwich-1.2.6.tar.gz", hash = "sha256:405cfd53a99374ff03aacdd7a86d6a07615feca072ed69721f49ae2ebaa3eab4", size = 1257895, upload-time = "2026-05-31T14:32:52.758Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/d9/e6272430ccf84739509f45006543a1babcecc55a70cbd296424901df8ed5/dulwich-1.2.10.tar.gz", hash = "sha256:106546960f592216315e56d32a92e72e67f1e86671335722f833f5ecd5b73314", size = 1310282, upload-time = "2026-07-07T00:52:05.986Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/ea/c54b0a87815e06baeb541c17e492c2e3fb7b9f216dc2033e3a356078270c/dulwich-1.2.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e103584421b7205f022bd413a324ff26905ffa84fcc1536f5787bf554d5d390b", size = 1400786, upload-time = "2026-05-31T14:32:01.994Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d1/5ca58eb2d1160d52ac2d109da1b4bd6c332a1a803fa6cb7ca7cda5f37431/dulwich-1.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e357d825b82e7fec2b83cd8e50f3c099c14c1070e1df961bfefb83943dc1582", size = 1383818, upload-time = "2026-05-31T14:32:03.444Z" }, - { url = "https://files.pythonhosted.org/packages/33/25/3dc9960cbdeef59fed4c07df52c17eacb3c5515b24cff64c524cdc75b563/dulwich-1.2.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:11b1f5a6a6075ab4f906dfb755c1d805c8c898ba4f4816b0fdb6123e113030ac", size = 1472506, upload-time = "2026-05-31T14:32:04.885Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/39dd7470d37609a62c66bb59d298f871fd835d37580cc870c5b8a66ea87e/dulwich-1.2.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:6d9720d591052730775dcbf450f0cd5b35162f4eeb4754337a5d763326481b2f", size = 1499334, upload-time = "2026-05-31T14:32:06.186Z" }, - { url = "https://files.pythonhosted.org/packages/48/f9/504b6e0c9f26bca62dc54cf5d6a65c807098856b6732279e37a9c034acab/dulwich-1.2.6-cp311-cp311-win32.whl", hash = "sha256:371394e2c6f3f9789cdc0abb965dae9bc62e79984b84f35339e9d466598c9fb0", size = 1068084, upload-time = "2026-05-31T14:32:07.466Z" }, - { url = "https://files.pythonhosted.org/packages/15/b6/ee75e1916984716cb57adf0d1f95e7b241d4accc4dc4d1ae3a9ddba1a411/dulwich-1.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:f887643cf1c7a04e898547bd9f0acf6654d772ebd153012433ef950315dcf776", size = 1081023, upload-time = "2026-05-31T14:32:08.889Z" }, - { url = "https://files.pythonhosted.org/packages/d9/80/496b2f8d584a7ba28519fd552d10c070498a76a17a92d288f1263e8e577d/dulwich-1.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:116ac7decb923a473540bf813c1ceb061bef07209fad5fb002d867f1907f9393", size = 1398028, upload-time = "2026-05-31T14:32:10.25Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ce/2ac1ccb8f5c039a93b3e6c1fc9f06ded05eb4adf7e934a643894389be755/dulwich-1.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6993ad48f92dc38a43e3c1bf25efb03a62fc2cf4db86a2e904b6c7176dafc3d5", size = 1336335, upload-time = "2026-05-31T14:32:11.64Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7d/7e1e736b9dfdb8aacf474c29d2b2fe331a23e1aa3741428a87280a73dbb9/dulwich-1.2.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:72512e2a22df6fb65ba7b66f5037046019a12343f6e9e54f42bcc4a68ab3d628", size = 1418127, upload-time = "2026-05-31T14:32:13.124Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/fc716cd97d35335a6bc02bea4e24bb1c1f0947731a421c2038b6c250c0b2/dulwich-1.2.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e995ad77b0685747bdb51f7a5cd7e6cb8efe73e29517b0f2c95fc2e6d10d5a90", size = 1442869, upload-time = "2026-05-31T14:32:14.532Z" }, - { url = "https://files.pythonhosted.org/packages/67/35/79dcfbafa5d3c6271da71a6b016e80f1a393a50c013958fd1d7c3375f284/dulwich-1.2.6-cp312-cp312-win32.whl", hash = "sha256:4940fbf7cb37870686c63dfc7682e1afdab0e55b663bb614572909b68e775d31", size = 1018595, upload-time = "2026-05-31T14:32:15.805Z" }, - { url = "https://files.pythonhosted.org/packages/63/9a/b33d7e6749417552fbf065fd734395a90b7b5d27a377149fbb837aea8127/dulwich-1.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c60ddc8206e04e8e08208eac80130004eff0d587c82d398beeca7330cade061f", size = 1033271, upload-time = "2026-05-31T14:32:17.27Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ea/f0d0aaf7c9e36f5490579a20ed37c85afd19e90177c2e270ff533d7fd533/dulwich-1.2.6-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:cdd15b8442b527575d733d90cfd6d3c4cbaebf989e2298b0cb57a7916c66254f", size = 1532486, upload-time = "2026-05-31T14:32:18.668Z" }, - { url = "https://files.pythonhosted.org/packages/14/4e/5c212c2dcc2d8c06cafdcdc7893d9516cb861ec277f25a0f058f98512d22/dulwich-1.2.6-cp313-cp313-android_21_x86_64.whl", hash = "sha256:dd2783352917b7cb3ab12b7c3f7757210d93af6df0bd2d876a8e5b53b2feb3eb", size = 1525768, upload-time = "2026-05-31T14:32:20.174Z" }, - { url = "https://files.pythonhosted.org/packages/f4/84/7ff849d4fe769cb6439fc50322381b7eb3d6e5d64da9e5d8337c985a7748/dulwich-1.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:204d14692fb1dd850ab773690f7530f4065f405e9e7dd3f85bdf92e9330ffa2d", size = 1396354, upload-time = "2026-05-31T14:32:21.52Z" }, - { url = "https://files.pythonhosted.org/packages/29/4d/2cb9662dd57417a11e5828f2b8f7607cfaccb42dcd9b6d69316755a5f5e0/dulwich-1.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:21e2e9b81ab04ad83f2d4101ac515ef56ee08d06fd853c1a7ac255f20bb49963", size = 1335031, upload-time = "2026-05-31T14:32:22.978Z" }, - { url = "https://files.pythonhosted.org/packages/2e/82/38ccfa7ee30c13d44734c5b1eb92ad0c95a96035319040e2c0b2011eee75/dulwich-1.2.6-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7b4a2f497718bfe1a3b21f933ee27c111b9cea560c0b2d8a6d939e1b5f297f79", size = 1417366, upload-time = "2026-05-31T14:32:24.295Z" }, - { url = "https://files.pythonhosted.org/packages/be/a1/239d52cbd94482c064821a0ccece888aac105a81f3f9719b9622c52fe6ec/dulwich-1.2.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5ff9f36c95deaf7eb5d6ccde4c68adbcb932a87e03c1b479a8d94d779e7cc5d2", size = 1442588, upload-time = "2026-05-31T14:32:25.731Z" }, - { url = "https://files.pythonhosted.org/packages/6e/92/739dc9e4d5da0b1c09b752f8aad94a518ff5eca7db2f5039ba7d5976b81f/dulwich-1.2.6-cp313-cp313-win32.whl", hash = "sha256:04252b107a1600325f5f0301dde8b5b62f5bb51a0467e360070baddbb4edcea7", size = 1018371, upload-time = "2026-05-31T14:32:27.147Z" }, - { url = "https://files.pythonhosted.org/packages/bb/28/626dc722ab20e0e5d0bf67e212494563f514790bc9c4b7c0133fdf491a5d/dulwich-1.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:6fd9911fb57ee2d6eefaf895df65e1139fbc911fa560e959b38feabe5f15003f", size = 1032986, upload-time = "2026-05-31T14:32:28.66Z" }, - { url = "https://files.pythonhosted.org/packages/5c/b9/bb2f8b1d668b3959b8471c2af0fa2dbce66e81b2defab1fcfde4cd5a0ac1/dulwich-1.2.6-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:cb1f8d658f36b2ac3982715dc3e49f0d741a3e5a8c40136bebb6d8493968aa12", size = 1533055, upload-time = "2026-05-31T14:32:30.048Z" }, - { url = "https://files.pythonhosted.org/packages/e0/66/1a3b4580af650a40adb6ef1dce6f9153fa1096c723e1abff32959a0a2c76/dulwich-1.2.6-cp314-cp314-android_24_x86_64.whl", hash = "sha256:ad4b6114440f9cf72315b173532ee3284f27a288b8a24bc27e45b2e54593720d", size = 1478589, upload-time = "2026-05-31T14:32:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/a3/34/55bc2440d1924710390ece68dc30496b0a0ef26228d9e02a60c82fc15ed1/dulwich-1.2.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:824b7f5b22b128c1e1ad7c655e9790e2d75c7ab1ba1e40a708024193f1dc47a3", size = 1351518, upload-time = "2026-05-31T14:32:32.644Z" }, - { url = "https://files.pythonhosted.org/packages/f3/31/befeb51ffd5047ea090ab18d2dc43525e6d528006eb7064135c2f1a1229a/dulwich-1.2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c187efaebb72146245ebcb872b89fdc99314fa37442119c5a5feb18af3f4b8a", size = 1335713, upload-time = "2026-05-31T14:32:33.995Z" }, - { url = "https://files.pythonhosted.org/packages/72/8f/2913d2ab1cff9722383b88be0e34f8a277075c5b5ca3fc822ac2192476d8/dulwich-1.2.6-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:79728d98e0ec184856d71fd0d55abbf5ac7345b5baea9f2d1533a4de9064e13d", size = 1420343, upload-time = "2026-05-31T14:32:35.57Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/53afa6cf1fb3bdf6669a73a6b1978f0adc2ac24f03fc414f45c5902e66e7/dulwich-1.2.6-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:c639a8c9fb7e745749f2dcbd5b63a82df2fc99cfe62e2c3654ec025a42d2e51f", size = 1442317, upload-time = "2026-05-31T14:32:37.216Z" }, - { url = "https://files.pythonhosted.org/packages/23/34/4119559608dfc4b68427d8e9e3892db7a4b2dbf7b3e7c5b6a390e0af560f/dulwich-1.2.6-cp314-cp314-win32.whl", hash = "sha256:dd2b66c915f1b22ca6533b48e8ee435800b25f74f419c40e1a92271666d8b297", size = 1026443, upload-time = "2026-05-31T14:32:39.064Z" }, - { url = "https://files.pythonhosted.org/packages/1e/5f/4de7960cc39cfbd629ad60c1ef631ed32e4b303d4228ad8f92847aa87f3e/dulwich-1.2.6-cp314-cp314-win_amd64.whl", hash = "sha256:82e8810e57f9651a624116e3fede33276f89406cb910f517b944105e284e6755", size = 1041672, upload-time = "2026-05-31T14:32:41.008Z" }, - { url = "https://files.pythonhosted.org/packages/72/19/01bcd25e4b2a8cf5f3507c4d02d6c999b70eb6d32eaff59c583e8e9c40c0/dulwich-1.2.6-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:794a85b8b9d4ad57d02c8cb455735419ac50c0f2e3d26d83873e34abee58cb1b", size = 1350601, upload-time = "2026-05-31T14:32:42.288Z" }, - { url = "https://files.pythonhosted.org/packages/14/9c/517d858ee9af0678e5605397d1b5ca6c49210b64508c91486893c9fc0e56/dulwich-1.2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:eb27a9ebe9029c872abadf4f9dcb18c9f6a4b7a4afe137f79a61df1ae59dc6bf", size = 1334723, upload-time = "2026-05-31T14:32:44.163Z" }, - { url = "https://files.pythonhosted.org/packages/58/e9/d8dd703c6c581eacf109427472c1c02955b8de70fb8154ad2fe0cf2e0540/dulwich-1.2.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:1c35c294acfc5a0a88d01d5db1abeba550bf6274bcc3fddbf8b365e9eea280da", size = 1461300, upload-time = "2026-05-31T14:32:45.475Z" }, - { url = "https://files.pythonhosted.org/packages/df/40/94c0d609206a770d3b4bc8dc30601e3c63642af637c5c12035e9645e0c85/dulwich-1.2.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:f682671a2e19b7b4caa572ff3073557de049a153946305e051a4f50bb0e5e1bd", size = 1491547, upload-time = "2026-05-31T14:32:47.12Z" }, - { url = "https://files.pythonhosted.org/packages/04/97/c18ab70ec27eed02f6ba7b00b3d6236657df7cabbc7175533ff5b62f2147/dulwich-1.2.6-cp314-cp314t-win32.whl", hash = "sha256:27db364f2f3cf5b0dddd44d6c2ae9a20f6021e2bae8b1268fa689076f0192244", size = 1024467, upload-time = "2026-05-31T14:32:48.476Z" }, - { url = "https://files.pythonhosted.org/packages/41/8c/7f49d256a813ab799fbce519a37857d75201c7c02b06b1a65fd989ec2ff2/dulwich-1.2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fae59c5e345f5ca234c85d157f1c7d5e0086126b45b5f7cfa66ffe41d049fdd6", size = 1083682, upload-time = "2026-05-31T14:32:49.881Z" }, - { url = "https://files.pythonhosted.org/packages/24/15/61bd455d33979584f19d3a6e0b49b49e0d891bc680fc8cc7b028aea7360d/dulwich-1.2.6-py3-none-any.whl", hash = "sha256:8d8175dbe4feaf62bcafc8708448bfe223b4dfc71609be25c0cf2b0962abc36c", size = 688260, upload-time = "2026-05-31T14:32:51.285Z" }, + { url = "https://files.pythonhosted.org/packages/55/11/8a2aa5f7220d9290e4c9ad4ac6d482d9b34762079331e8e02d835329f552/dulwich-1.2.10-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f6d989605955bfbb678425a2e8710100ef35960340bfb55faaca902006c69163", size = 1378353, upload-time = "2026-07-07T00:51:10.307Z" }, + { url = "https://files.pythonhosted.org/packages/7a/66/859b121c1f18fa32833a239beadd67cf11df89974ff223b28d9ff53f4f2f/dulwich-1.2.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af4a750597d1a6e77bca74b9246bf84b62a9bfa7e48783af4ed0e9f04f0dbfbd", size = 1371855, upload-time = "2026-07-07T00:51:11.999Z" }, + { url = "https://files.pythonhosted.org/packages/85/66/1833afc2444fc70e6e79458169275687aef858ea1c6a8198e2b7f97da27d/dulwich-1.2.10-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6c715289e8c5667978ff28a1847736633766ca1c7082144ba0ee95e459be3f97", size = 1493934, upload-time = "2026-07-07T00:51:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1f/a200b9071a4d9dce6ce0033654d63ee389be2d4de432d1d7028dccdbf8d5/dulwich-1.2.10-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f21c7f2dc4de8353b59b82bc0245838f3f94ff9332b71f6394642e3bb012c71b", size = 1523369, upload-time = "2026-07-07T00:51:15.338Z" }, + { url = "https://files.pythonhosted.org/packages/11/1b/e36deccfa78f674e7c4d7482b2a710581a1161b65080dd6b1daac17c5b42/dulwich-1.2.10-cp311-cp311-win32.whl", hash = "sha256:ca1fbe5f4b2ee48154b24c68eeb689b848394a857f7cb3dea3de0b86c464454b", size = 1043413, upload-time = "2026-07-07T00:51:16.900Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/fb78d06d58486ea3bef2c87af17ea0b106d9863e89664cc834cea060af6e/dulwich-1.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:9874964ac78713c640c42c9dc27a74dd2d4169d99821a57301d37e5673409d27", size = 1103445, upload-time = "2026-07-07T00:51:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/73/fb/af9504fed5f023194143bd059132729aad00f4608f4ed785dc1f94ff500d/dulwich-1.2.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4bf4671061aa98e7f4d420fe5238b2ca2cb2a00a280c8f549fa92d34569971a8", size = 1371347, upload-time = "2026-07-07T00:51:20.811Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3e/aff9e8b9d07f3afac9c618cd2e7529e4695375a64ebd07831462bb78c946/dulwich-1.2.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43315f1e95f7d5c1a6f2695f5534263b80678ccbfd9756101f07759a4e65850e", size = 1356141, upload-time = "2026-07-07T00:51:22.108Z" }, + { url = "https://files.pythonhosted.org/packages/89/8d/03c2692896241b94d04cbc8f4db662458864e13604e5295baf96a2b647db/dulwich-1.2.10-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:699edc94fddd0a2346e41e405fd7c537072d75cb9ddb39bd47f2e8fbd56d1e4e", size = 1437999, upload-time = "2026-07-07T00:51:23.500Z" }, + { url = "https://files.pythonhosted.org/packages/43/75/b37e1c9548d7b9355971e462acf5da795c9539d8b330f4495ddf0244d99a/dulwich-1.2.10-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dc7c46cfeb40022d2fca9d23c3ca1aba602ee99bfb6554278f05f2aa890a0e", size = 1463581, upload-time = "2026-07-07T00:51:25.060Z" }, + { url = "https://files.pythonhosted.org/packages/32/a0/2de9d8bfd975b79e51c40394d8fb627dbffc97a4c89778eb3314b304ea6e/dulwich-1.2.10-cp312-cp312-win32.whl", hash = "sha256:9c01a6b29cedb054a16fa04adf834a6531a39450888fc7ce2b3bf0fb6cba70c4", size = 1082677, upload-time = "2026-07-07T00:51:26.578Z" }, + { url = "https://files.pythonhosted.org/packages/d1/67/49e913c54d7723c5a5bb12a38552a6db5cb691631326882ab2208a5186fa/dulwich-1.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:96981c6704def4fd0f91fb9be9f613cf99ba327e2a655b8ef987f5f5245420f3", size = 1053703, upload-time = "2026-07-07T00:51:28.023Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/6389b4209788fe46e4943492158f497ac6f7c485b83c70a4f79546d0ccc5/dulwich-1.2.10-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:bd859654ed2e8c61c03066b739fc758b70edfcfce41344fae07f5f7f41b998b4", size = 1510472, upload-time = "2026-07-07T00:51:29.709Z" }, + { url = "https://files.pythonhosted.org/packages/b7/5c/07c64540fc713057351db05a021deec6518f57e8393e734d058be8def427/dulwich-1.2.10-cp313-cp313-android_24_x86_64.whl", hash = "sha256:adf9a780add1b6da7e6df1b609afca66733380188a3a257ea340bc080ef4637c", size = 1500010, upload-time = "2026-07-07T00:51:31.092Z" }, + { url = "https://files.pythonhosted.org/packages/20/af/4007763bde53f768ac9736f81f5af2658808181024d2afe1b99e6bb34826/dulwich-1.2.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f337fd7dffe5e6cbff5e9a4694339b7575ed948babcc7675321efac075cb0a0e", size = 1370987, upload-time = "2026-07-07T00:51:32.553Z" }, + { url = "https://files.pythonhosted.org/packages/23/da/336440eb9bf6ca889a99c0c1bab37763714e2f56fe9553fe23f269560c58/dulwich-1.2.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ded6744fcd2fb0e1f66c811492aefcdbee169010057f2a2e034a463eb0118c80", size = 1355513, upload-time = "2026-07-07T00:51:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e4/cd27a143488489c4d40b3b9fefa2fa189c49920ac226c6b11475892f6576/dulwich-1.2.10-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a16ea1a6d96db53a0089f7f6c4e6b942eff143e4de26b786aa894bf978ac148d", size = 1437157, upload-time = "2026-07-07T00:51:35.691Z" }, + { url = "https://files.pythonhosted.org/packages/45/03/44017e9b958238612aa17f8a6ad9af4ff63536cb99c8d8f4e210a8aa6866/dulwich-1.2.10-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:dfe2683285a7363a0d4b16f7f6fc9ee8a57a7b1cb1c390695475b76d8ce1b0f5", size = 1462033, upload-time = "2026-07-07T00:51:37.119Z" }, + { url = "https://files.pythonhosted.org/packages/75/74/6f2ddceed5d8fa3f49235fe33afd55d92a986a3bd5ce3dbcba42907d96d4/dulwich-1.2.10-cp313-cp313-win32.whl", hash = "sha256:0840968ec6636a677b48bc6e38bf4ce2326e8f0d60cd9c9753cceeff5e23e92c", size = 1082412, upload-time = "2026-07-07T00:51:38.531Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/bf8c3e47edb11798db7303b255d63db916877042e848808a6b5942e6624e/dulwich-1.2.10-cp313-cp313-win_amd64.whl", hash = "sha256:a2e884a93452e8e1fb4a4886d14b328bae65c7a2fa88466c0b38faa63204f34d", size = 1053583, upload-time = "2026-07-07T00:51:39.950Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2d/643fcf28fb2f3118ec6b558695e4e18b0991edf64d4447bc6c5105465d4f/dulwich-1.2.10-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:fd86069664383fc456eeb291d67dc541c7f6ab163c59da1f8e18c72283f120c5", size = 1512387, upload-time = "2026-07-07T00:51:41.629Z" }, + { url = "https://files.pythonhosted.org/packages/b7/61/c57fd318bc943f294420353fa5544d54fb8984abdd95ef24a21d8d0f5b3d/dulwich-1.2.10-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e445229de65ad6c3a8e2d9934fb807453c09b94f1469d32804e19fdfdcf6fd94", size = 1502540, upload-time = "2026-07-07T00:51:43.571Z" }, + { url = "https://files.pythonhosted.org/packages/03/8e/4b284074e199193dbc1cd8d26dcc92ab8f84fb73654c05c290619cd3ed24/dulwich-1.2.10-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:09c36bed6f2695cf34e606c403a00c6511f87f31ed20cac68f0bacf6df21cc4b", size = 1375734, upload-time = "2026-07-07T00:51:45.642Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6c/b465e1b60ab4675cbeeb8977c871c4b773bf0667d1a33dfa879e446c86c8/dulwich-1.2.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea42aa414a53d71ae0e294003a10b17ad454ff73d345a82e47da93e977fd2ff1", size = 1358916, upload-time = "2026-07-07T00:51:47.098Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e8/7efcc79c58153dff1c96dc8992a161d28288a7381eb4591c49712d112f61/dulwich-1.2.10-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:5b60380225742d7292703e573ce5a86be6f69fb9088ef2965c43830479df9f90", size = 1485761, upload-time = "2026-07-07T00:51:48.407Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f7/609f2fcfafc515f25948f14c349467d2c3c72a035e65470a8cc4fa4fa7da/dulwich-1.2.10-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:c321650fc9fa0b3e614607f0a229523491153d0ab00502366ac93b022d3c7f6c", size = 1513339, upload-time = "2026-07-07T00:51:49.884Z" }, + { url = "https://files.pythonhosted.org/packages/90/53/df8b6c6168e7e1bab5b33dbee0c82d99ab94021493e5d1f1445d15eb1c61/dulwich-1.2.10-cp314-cp314-win32.whl", hash = "sha256:834df23dca29e5adc5310b1b73eb573ad805fd0f39987e86afda2d81bfe24ab1", size = 1046718, upload-time = "2026-07-07T00:51:51.403Z" }, + { url = "https://files.pythonhosted.org/packages/5a/da/5497cc3be68fa62626da0111b88dfa1a296907fe67c3cd57ff65bd67c7bf/dulwich-1.2.10-cp314-cp314-win_amd64.whl", hash = "sha256:abd8ffeec9c151cea62aafd25260ac7141a7ea56ce363dfc72cddf63396e8d02", size = 1106937, upload-time = "2026-07-07T00:51:52.853Z" }, + { url = "https://files.pythonhosted.org/packages/1e/af/b88671fdc6cb3f0732d0bef0bfdc2937370dcef605c14d90d2d63f7b7c34/dulwich-1.2.10-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d5fd557539c982aabe15f7a14cff3d8786cdb62578d6f90f7871379fa3453260", size = 1421051, upload-time = "2026-07-07T00:51:55.650Z" }, + { url = "https://files.pythonhosted.org/packages/17/0d/0d5d1f92c3589772d6f367be9251c417e35890ca7b5e461ed90f11e1615f/dulwich-1.2.10-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:66cb00f1298e4e0dd254ff6a68554577020b4b810b4a31083409baea62f8b1df", size = 1359050, upload-time = "2026-07-07T00:51:57.002Z" }, + { url = "https://files.pythonhosted.org/packages/bf/23/0b1a354cbaa574596e0bde8171b452ec7882765fcef283dcd2f4d8bf93b0/dulwich-1.2.10-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:eca947467a28992088a0461c408791b405153294887accb92f64d60f9cc04758", size = 1440306, upload-time = "2026-07-07T00:51:58.390Z" }, + { url = "https://files.pythonhosted.org/packages/e6/14/0178b5f8a9ccaa7ae434d0c931d2c792b1243a1ea2d98a08646a936f132e/dulwich-1.2.10-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7712da64d7cad219c77aa4216d46a1b14876ed078b2a88621462d2834f4f85b3", size = 1466689, upload-time = "2026-07-07T00:51:59.738Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ac/10ba0dbe9cf3112d3b6fc8305b731798734017762200ac9fb75a0fc297be/dulwich-1.2.10-cp314-cp314t-win32.whl", hash = "sha256:52cfbd70f178b5ad770b9a1aee8a757aeda0e7282ae72cd546a98e6b9b0816cb", size = 1046430, upload-time = "2026-07-07T00:52:01.532Z" }, + { url = "https://files.pythonhosted.org/packages/34/d3/2bc63e9ef4a3248595b8f02bfee22bf9df77adb181a53716fd81305d2665/dulwich-1.2.10-cp314-cp314t-win_amd64.whl", hash = "sha256:38478aa5d1821fd3d41e1ec7c65fb9a55d302d041635079db11118b79109054f", size = 1063788, upload-time = "2026-07-07T00:52:03.026Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f5/efdcbd50a4480083d8d049ea1c49095097802599f7ecd2dc92942af7f4c4/dulwich-1.2.10-py3-none-any.whl", hash = "sha256:b9f18afbe8e6f6a380b199df3d2da14312b4eac4ee34ee77c6f61c56bd2693ed", size = 711548, upload-time = "2026-07-07T00:52:04.539Z" }, ] [[package]] @@ -374,7 +374,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "dulwich", specifier = ">=1.2.5" }, + { name = "dulwich", specifier = ">=1.2.10" }, { name = "msgspec", specifier = ">=0.19.0" }, { name = "pyyaml", specifier = ">=6.0" }, { name = "sqlalchemy", marker = "extra == 'sql'", specifier = ">=2.0" },