diff --git a/mergin/client_pull.py b/mergin/client_pull.py index 7715a40..a1f9627 100644 --- a/mergin/client_pull.py +++ b/mergin/client_pull.py @@ -543,9 +543,10 @@ def pull_project_async(mc, directory) -> Optional[PullJob]: # find corresponding local delta item local_item = next((i for i in local_delta if i.path == change.path), None) local_item_change = local_item.type if local_item else None + local_item_checksum = local_item.checksum if local_item else None # compare server and local changes to decide what to do in pull - pull_action_type = mp.get_pull_action(change.type, local_item_change) + pull_action_type = mp.get_pull_action(change.type, local_item_change, change.checksum, local_item_checksum) if not pull_action_type: continue # no action needed diff --git a/mergin/merginproject.py b/mergin/merginproject.py index 0eaeb51..f9312fa 100644 --- a/mergin/merginproject.py +++ b/mergin/merginproject.py @@ -504,7 +504,11 @@ def get_pull_delta(self, server_files: List[Dict], server_version: str) -> Proje return ProjectDelta(to_version=server_version, changes=result) def get_pull_action( - self, server_change: DeltaChangeType, local_change: Optional[DeltaChangeType] = None + self, + server_change: DeltaChangeType, + local_change: Optional[DeltaChangeType] = None, + server_checksum: Optional[str] = None, + local_checksum: Optional[str] = None, ) -> Optional[PullActionType]: """ Determine pull actions for files by comparing server_change and local_change. @@ -520,11 +524,17 @@ def get_pull_action( self.log.critical(f"Invalid combination of changes: server {server_change}, local {local_change}") raise ClientError(f"Invalid combination of changes: server {server_change}, local {local_change}") + checksum_conflict = server_checksum != local_checksum if server_checksum and local_checksum else False + pull_action_map = { (DeltaChangeType.CREATE, None): PullActionType.COPY, - (DeltaChangeType.CREATE, DeltaChangeType.CREATE): PullActionType.COPY_CONFLICT, + (DeltaChangeType.CREATE, DeltaChangeType.CREATE): ( + PullActionType.COPY_CONFLICT if checksum_conflict else None + ), (DeltaChangeType.UPDATE, None): PullActionType.COPY, - (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE): PullActionType.COPY_CONFLICT, + (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE): ( + PullActionType.COPY_CONFLICT if checksum_conflict else None + ), (DeltaChangeType.UPDATE, DeltaChangeType.DELETE): PullActionType.COPY, (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE_DIFF): PullActionType.COPY_CONFLICT, (DeltaChangeType.UPDATE, DeltaChangeType.CREATE): PullActionType.COPY_CONFLICT, diff --git a/mergin/test/test_mergin_project.py b/mergin/test/test_mergin_project.py index e3dbdcb..6da69e7 100644 --- a/mergin/test/test_mergin_project.py +++ b/mergin/test/test_mergin_project.py @@ -82,27 +82,29 @@ def test_get_pull_action_valid(): with tempfile.TemporaryDirectory() as tmp_dir: mp = MerginProject(tmp_dir) - # Test cases: (server_change, local_change, expected_action) + # Test cases: (server_change, local_change, server_checksum, local_checksum, expected_action) test_cases = [ - (DeltaChangeType.CREATE, None, PullActionType.COPY), - (DeltaChangeType.CREATE, DeltaChangeType.CREATE, PullActionType.COPY_CONFLICT), - (DeltaChangeType.UPDATE, None, PullActionType.COPY), - (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE, PullActionType.COPY_CONFLICT), - (DeltaChangeType.UPDATE, DeltaChangeType.DELETE, PullActionType.COPY), - (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE_DIFF, PullActionType.COPY_CONFLICT), - (DeltaChangeType.UPDATE, DeltaChangeType.CREATE, PullActionType.COPY_CONFLICT), - (DeltaChangeType.UPDATE_DIFF, None, PullActionType.APPLY_DIFF_NO_REBASE), - (DeltaChangeType.UPDATE_DIFF, DeltaChangeType.UPDATE, PullActionType.COPY_CONFLICT), - (DeltaChangeType.UPDATE_DIFF, DeltaChangeType.DELETE, PullActionType.COPY), - (DeltaChangeType.UPDATE_DIFF, DeltaChangeType.UPDATE_DIFF, PullActionType.APPLY_DIFF_REBASE), - (DeltaChangeType.DELETE, None, PullActionType.DELETE), - (DeltaChangeType.DELETE, DeltaChangeType.UPDATE, None), - (DeltaChangeType.DELETE, DeltaChangeType.DELETE, None), - (DeltaChangeType.DELETE, DeltaChangeType.UPDATE_DIFF, None), + (DeltaChangeType.CREATE, None, None, None, PullActionType.COPY), + (DeltaChangeType.CREATE, DeltaChangeType.CREATE, "c1", "c2", PullActionType.COPY_CONFLICT), + (DeltaChangeType.CREATE, DeltaChangeType.CREATE, "c1", "c1", None), + (DeltaChangeType.UPDATE, None, None, None, PullActionType.COPY), + (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE, "c1", "c2", PullActionType.COPY_CONFLICT), + (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE, "c1", "c1", None), + (DeltaChangeType.UPDATE, DeltaChangeType.DELETE, "c1", "c2", PullActionType.COPY), + (DeltaChangeType.UPDATE, DeltaChangeType.UPDATE_DIFF, "c1", "c2", PullActionType.COPY_CONFLICT), + (DeltaChangeType.UPDATE, DeltaChangeType.CREATE, "c1", "c2", PullActionType.COPY_CONFLICT), + (DeltaChangeType.UPDATE_DIFF, None, "c1", "c2", PullActionType.APPLY_DIFF_NO_REBASE), + (DeltaChangeType.UPDATE_DIFF, DeltaChangeType.UPDATE, "c1", "c2", PullActionType.COPY_CONFLICT), + (DeltaChangeType.UPDATE_DIFF, DeltaChangeType.DELETE, "c1", "c2", PullActionType.COPY), + (DeltaChangeType.UPDATE_DIFF, DeltaChangeType.UPDATE_DIFF, "c1", "c2", PullActionType.APPLY_DIFF_REBASE), + (DeltaChangeType.DELETE, None, "c1", "c2", PullActionType.DELETE), + (DeltaChangeType.DELETE, DeltaChangeType.UPDATE, None, None, None), + (DeltaChangeType.DELETE, DeltaChangeType.DELETE, None, None, None), + (DeltaChangeType.DELETE, DeltaChangeType.UPDATE_DIFF, None, None, None), ] - for server_change, local_change, expected_action in test_cases: - action = mp.get_pull_action(server_change, local_change) + for server_change, local_change, server_checksum, local_checksum, expected_action in test_cases: + action = mp.get_pull_action(server_change, local_change, server_checksum, local_checksum) assert ( action == expected_action ), f"Failed for {server_change}, {local_change}. Expected {expected_action}, got {action}" @@ -123,7 +125,7 @@ def test_get_pull_action_fatal(): for server_change, local_change in fatal_cases: with pytest.raises(ClientError, match="Invalid combination of changes"): - mp.get_pull_action(server_change, local_change) + mp.get_pull_action(server_change, local_change, None, None) def test_get_pull_delta():