Skip to content

Fix sync loop for too long path - #316

Merged
harminius merged 16 commits into
masterfrom
fix/sync_loop_for_too_long_path
Oct 22, 2026
Merged

harminius merged 16 commits into
masterfrom
fix/sync_loop_for_too_long_path

Conversation

@harminius

@harminius harminius commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Fixes MerginMaps/qgis-plugin#932

This adds an extended-path prefix to all Python filesystem operations on Windows so that it can work with files&folder > 260 characters.

Verified on Windows with OS long-path support off (default install), project path 317 chars: https://github.com/MerginMaps/python-api-client/actions/runs/39755718281

code geodiff client file handling geodiff push creates diff
master 2.3.1 fail fail fail
master 2.3.2 fail pass fail
this PR 2.3.1 pass fail fail (full upload)
this PR 2.3.2 pass pass pass

The \\?\ prefix is applied only at the filesystem call and never appears in the push payload. The geodiff half needs pygeodiff 2.3.2, which is bumped in a separate PR.

@harminius harminius changed the title Fix sync loop for too long path WIP: Fix sync loop for too long path Aug 11, 2026
@harminius

Copy link
Copy Markdown
Contributor Author

client-log.txt

@harminius

Copy link
Copy Markdown
Contributor Author

db paths look fine
Screenshot from 2026-08-17 21-32-34

@harminius harminius changed the title WIP: Fix sync loop for too long path Fix sync loop for too long path Aug 17, 2026
@coveralls

coveralls commented Aug 17, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 39755347633

Coverage increased (+0.001%) to 84.046%

Details

  • Coverage increased (+0.001%) from the base build.
  • Patch coverage: 13 uncovered changes across 5 files (116 of 129 lines covered, 89.92%).
  • 2 coverage regressions across 2 files.

Uncovered Changes

File Changed Covered %
mergin/utils.py 19 12 63.16%
mergin/client.py 11 9 81.82%
mergin/client_pull.py 18 16 88.89%
mergin/fs.py 26 25 96.15%
mergin/merginproject.py 45 44 97.78%
Total (7 files) 129 116 89.92%

Coverage Regressions

2 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
mergin/client_pull.py 1 83.27%
mergin/client.py 1 88.75%

Coverage Stats

Coverage Status
Relevant Lines: 6030
Covered Lines: 5068
Line Coverage: 84.05%
Coverage Strength: 0.84 hits per line

💛 - Coveralls

Comment thread mergin/utils.py Resolved
Comment thread mergin/client.py Resolved
@harminius
harminius requested a review from varmar05 August 26, 2026 14:34
@harminius

harminius commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Let's try to use \\?\-prefixed paths for geodiff calls and other (all) file I/O.

@harminius

Copy link
Copy Markdown
Contributor Author

PR history:

Debugging info:
Pull works as py-client saves the diffs to tmp dir
However, there is always a check if there's something to push after the pull.

Push treats every gpkg with editing mode enabled as well as with original vs. current gpkg checksum mismatch (which is quite common) as potentially updated, and we rely on geodiff to calculate the real changes. Geodiff creates an empty diff file even when there is no change.
Geodiff's create_changeset() fails because it's not possible to create a file due to the Windows path length limit. It then pushes the whole file. Then we want to apply the push changes to the basefile. So geodiff removes the original geopackage and copies the current copy, which apparently changes the checksum, and we're caught in the loop. ➿

Site Feature
merginproject.py get_push_changes push
merginproject.py get_local_delta pull, local changes detection
client_pull.py get_download_diff_files pull, v1 fallback
client_pull.py pull_project_async v2 loop pull, v2
client_pull.py download_diffs_async get_file_diff / version viewer / reports
This is a minimal fix for the issue. There are other enhancements/issue mitigations:
  1. Shorten diff name
    Store diff files locally under a short, flat name, decoupled from the file's path in the project — e.g. .mergin/diffs/ instead of .mergin/-diff-. Fixes works esp. for files nested deep in the subfolders. This does not require server changes - the server doesn't care what we call the local file.
Path Length
Live file <rel> P + R + 1
Basefile .mergin<rel> — already mandatory today P + R + 9
Diff today .mergin<rel>-diff- P + R + 51
Diff flattened .mergin\diffs<uuid> P + 51 (constant)
Same, 16-char id P + 31 (constant)
  1. Safeguard for any infinite sync loop
    sync_project_generator's while has_changes has no iteration limit. Track, per file, how many consecutive iterations it was pushed as a full upload with no diff; if the same versioned file does this N times in a row (e.g. 2–3), raise a ClientError naming the file instead of continuing. This is cause-agnostic - it also protects against any other future reason a changeset can't be computed.
  1. Long path error workaround
    Use \\?\-prefixed paths for geodiff calls and other file I/O.

4. Use a temp folder for push diffs, as it is used for pull diffs already.

@harminius
harminius requested review from MarcelGeo and varmar05 and removed request for MarcelGeo and varmar05 September 10, 2026 11:13
@harminius
harminius merged commit 9f87874 into master Oct 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sync loop path to diff files in .mergin folder exceeds 260 characters

3 participants