Skip to content

Commit 2d38d24

Browse files
committed
Escape for windows long path
1 parent 6cc7fc6 commit 2d38d24

7 files changed

Lines changed: 150 additions & 130 deletions

File tree

mergin/client.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
int_version,
6868
is_version_acceptable,
6969
normalize_role,
70+
long_path,
7071
)
7172
from .version import __version__
7273

@@ -1237,7 +1238,7 @@ def get_file_diff(self, project_dir, file_path, output_diff, version_from, versi
12371238
# collect required versions from the cache
12381239
diffs = []
12391240
for v in versions_to_fetch[1:]:
1240-
diffs.append(mp.fpath_cache(file_history["history"][v]["diff"]["path"], v))
1241+
diffs.append(long_path(mp.fpath_cache(file_history["history"][v]["diff"]["path"], v)))
12411242

12421243
# concatenate diffs, if needed
12431244
output_dir = os.path.dirname(output_diff)
@@ -1377,13 +1378,15 @@ def reset_local_changes(self, directory: str, files_to_reset: typing.List[str] =
13771378
# remove all added files
13781379
for file in push_changes["added"]:
13791380
if all_files or file["path"] in files_to_reset:
1380-
os.remove(mp.fpath(file["path"]))
1381+
os.remove(long_path(mp.fpath(file["path"])))
13811382

13821383
# update files get override with previous version
13831384
for file in push_changes["updated"]:
13841385
if all_files or file["path"] in files_to_reset:
13851386
if mp.is_versioned_file(file["path"]):
1386-
mp.geodiff.make_copy_sqlite(mp.fpath_meta(file["path"]), mp.fpath(file["path"]))
1387+
mp.geodiff.make_copy_sqlite(
1388+
long_path(mp.fpath_meta(file["path"])), long_path(mp.fpath(file["path"]))
1389+
)
13871390
else:
13881391
files_download.append(file["path"])
13891392

mergin/client_pull.py

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from .common import CHUNK_SIZE, ClientError, DeltaChangeType, PullActionType
2626
from .models import ProjectDelta, ProjectDeltaChange, PullAction
2727
from .merginproject import MerginProject
28-
from .utils import cleanup_tmp_dir, save_to_file, is_path_too_long
28+
from .utils import cleanup_tmp_dir, save_to_file, long_path
2929
from typing import List, Optional
3030

3131
# status = download_project_async(...)
@@ -93,7 +93,9 @@ def __init__(self, file_path, size, version, diff_only, part_index, download_fil
9393
self.version = version # version of the file ("v123")
9494
self.diff_only = diff_only # whether downloading diff or full version
9595
self.part_index = part_index # index of the chunk
96-
self.download_file_path = download_file_path # full path to a temporary file which will receive the content
96+
self.download_file_path = long_path(
97+
download_file_path
98+
) # full path to a temporary file which will receive the content
9799

98100
def __repr__(self):
99101
return "<DownloadQueueItem path={} version={} diff_only={} part_index={} size={} dest={}>".format(
@@ -128,7 +130,9 @@ class DownloadDiffQueueItem:
128130

129131
def __init__(self, diff_id, download_file_path):
130132
self.diff_id = diff_id # relative path to the file within project
131-
self.download_file_path = download_file_path # full path to a temporary file which will receive the content
133+
self.download_file_path = long_path(
134+
download_file_path
135+
) # full path to a temporary file which will receive the content
132136
self.size = 0 # size of the item in bytes
133137

134138
def __repr__(self):
@@ -157,7 +161,7 @@ class DownloadFile:
157161
"""
158162

159163
def __init__(self, dest_file, downloaded_items: typing.List[DownloadQueueItem], size_check=True):
160-
self.dest_file = dest_file # full path to the destination file to be created
164+
self.dest_file = long_path(dest_file) # full path to the destination file to be created
161165
self.downloaded_items = downloaded_items # list of pieces of the destination file to be merged
162166
self.size_check = size_check # whether we want to do merged file size check
163167

@@ -196,7 +200,7 @@ def get_download_items(
196200

197201
items = []
198202
for part_index in range(chunks):
199-
download_file_path = os.path.join(file_dir, basename + ".{}".format(part_index))
203+
download_file_path = long_path(os.path.join(file_dir, basename + ".{}".format(part_index)))
200204
size = min(CHUNK_SIZE, file_size - part_index * CHUNK_SIZE)
201205
items.append(DownloadQueueItem(file_path, size, file_version, diff_only, part_index, download_file_path))
202206

@@ -419,7 +423,7 @@ def apply(self, directory, mp):
419423
# Make a copy of the file to meta dir only if there is no user-specified path for the file.
420424
# destination_file is None for full project download and takes a meaningful value for a single file download.
421425
if mp.is_versioned_file(self.file_path) and self.destination_file is None:
422-
mp.geodiff.make_copy_sqlite(mp.fpath(self.file_path), mp.fpath_meta(self.file_path))
426+
mp.geodiff.make_copy_sqlite(long_path(mp.fpath(self.file_path)), long_path(mp.fpath_meta(self.file_path)))
423427

424428

425429
class PullJob:
@@ -479,13 +483,7 @@ def get_download_diff_files(delta_item: ProjectDeltaChange, target_dir: str) ->
479483
result = []
480484

481485
for diff in delta_item.diffs:
482-
dest_file_path = os.path.normpath(os.path.join(target_dir, diff.id))
483-
if is_path_too_long(dest_file_path):
484-
raise ClientError(
485-
f"Cannot download diff for '{delta_item.path}': diff file path is too long "
486-
f"({len(dest_file_path)} characters) for this OS: {dest_file_path}\n"
487-
"Move the project to a directory with a shorter path and try again."
488-
)
486+
dest_file_path = long_path(os.path.normpath(os.path.join(target_dir, diff.id)))
489487
download_items = get_download_items(delta_item.path, diff.size, diff.version, target_dir, diff.id, True)
490488
result.append(DownloadFile(dest_file_path, download_items))
491489
return result
@@ -561,7 +559,7 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
561559
pull_action_type == PullActionType.COPY_CONFLICT and change.type == DeltaChangeType.UPDATE_DIFF
562560
):
563561
basefile = mp.fpath_meta(change.path)
564-
if not os.path.exists(basefile):
562+
if not os.path.exists(long_path(basefile)):
565563
# The basefile does not exist for some reason. This should not happen normally (maybe user removed the file
566564
# or we removed it within previous pull because we failed to apply patch the older version for some reason).
567565
# But it's not a problem - we will download the newest version and we're sorted.
@@ -580,15 +578,12 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
580578
# if we have conflict and diff update, download the diff files
581579
if v2_pull_enabled:
582580
# using v2 endpoint to download diff files, without chunks. Then we are creating DownloadDiffQueueItem instances for each diff file.
583-
for diff_item in change.diffs:
584-
diff_path = os.path.join(tmp_dir.name, diff_item.id)
585-
if is_path_too_long(diff_path):
586-
raise ClientError(
587-
f"Cannot download diff for '{change.path}': diff file path is too long "
588-
f"({len(diff_path)} characters) for this OS: {diff_path}\n"
589-
"Move the project to a directory with a shorter path and try again."
590-
)
591-
diff_files.append(DownloadDiffQueueItem(diff_item.id, diff_path))
581+
diff_files.extend(
582+
[
583+
DownloadDiffQueueItem(diff_item.id, os.path.join(tmp_dir.name, diff_item.id))
584+
for diff_item in change.diffs
585+
]
586+
)
592587
basefiles_to_patch.append((change.path, [diff.id for diff in change.diffs]))
593588

594589
else:
@@ -731,7 +726,7 @@ def pull_project_finalize(job: PullJob):
731726
basefile = job.mp.fpath_meta(file_path)
732727
server_file = job.mp.fpath(file_path, job.tmp_dir.name)
733728

734-
shutil.copy(basefile, server_file)
729+
shutil.copy(long_path(basefile), long_path(server_file))
735730
diffs = [job.mp.fpath(f, job.tmp_dir.name) for f in file_diffs]
736731
patch_error = job.mp.apply_diffs(server_file, diffs)
737732
if patch_error:
@@ -744,7 +739,7 @@ def pull_project_finalize(job: PullJob):
744739
job.mp.log.error("Diffs we were applying: " + str(diffs))
745740
job.mp.log.error("Removing basefile because it would be corrupted anyway...")
746741
job.mp.log.info("--- pull aborted")
747-
os.remove(basefile)
742+
os.remove(long_path(basefile))
748743
raise ClientError("Cannot patch basefile {}! Please try syncing again.".format(basefile))
749744
conflicts = []
750745
job.mp.log.info(f"--- applying pull actions {job.pull_actions}")
@@ -838,13 +833,7 @@ def download_diffs_async(mc, project_directory, file_path, versions):
838833
download_path=diff.get("path"),
839834
diff_only=True,
840835
)
841-
dest_file_path = mp.fpath_cache(diff["path"], version=file["version"])
842-
if is_path_too_long(dest_file_path):
843-
raise ClientError(
844-
f"Cannot download diff for '{file.get('path')}': diff file path is too long "
845-
f"({len(dest_file_path)} characters) for this OS: {dest_file_path}\n"
846-
"Move the project to a directory with a shorter path and try again."
847-
)
836+
dest_file_path = long_path(mp.fpath_cache(diff["path"], version=file["version"]))
848837
if os.path.exists(dest_file_path):
849838
continue
850839
download_files.append(DownloadFile(dest_file_path, items))

mergin/client_push.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
)
3535
from .merginproject import MerginProject, pygeodiff
3636
from .editor import filter_changes
37-
from .utils import get_data_checksum, cleanup_tmp_dir
37+
from .utils import get_data_checksum, cleanup_tmp_dir, long_path
3838

3939
POST_JSON_HEADERS = {"Content-Type": "application/json"}
4040

@@ -114,7 +114,7 @@ def upload_chunk_v2_api(self, data: ByteString, checksum: str):
114114
self.mc.upload_chunks_cache.add(checksum, self.server_chunk_id)
115115

116116
def upload_blocking(self):
117-
with open(self.file_path, "rb") as file_handle:
117+
with open(long_path(self.file_path), "rb") as file_handle:
118118
file_handle.seek(self.chunk_index * UPLOAD_CHUNK_SIZE)
119119
data = file_handle.read(UPLOAD_CHUNK_SIZE)
120120
checksum_str = get_data_checksum(data)
@@ -507,7 +507,7 @@ def remove_diff_files(job: UploadJob) -> None:
507507
for change in job.changes.updated:
508508
diff = change.get_diff()
509509
if diff:
510-
diff_file = job.mp.fpath_meta(diff.path)
510+
diff_file = long_path(job.mp.fpath_meta(diff.path))
511511
if os.path.exists(diff_file):
512512
os.remove(diff_file)
513513

0 commit comments

Comments
 (0)