Skip to content

Conversation

@MarcelGeo
Copy link
Contributor

@MarcelGeo MarcelGeo commented Dec 8, 2025

Resolved #280

  • new pull mechanism using /delta endpoint, where pull changes between current version and server are calculated from delta items
  • tests for both v2 and v1 features
  • projectinfo v2 compatibility test

Fixes

  • handle conflicting copy of versioned files properly without rebase logic

Pull logic updates

  • in pull_project_async is check if server supports v2_pull

  • If supports: get pull delta from server

  • If not: calculate pull delta from files and projects config.json

  • Then calculate delta items between local files and server (local delta) - delta needs to be applied to server

  • Compare pull_delta and local_delta -> PullAction with actions needs to be done after files are downloaded from a server defined in PullActionType enum

  • Starting download

  • After download is finished -> call pull_project_finalize

  • Method apply_pull_actions is then responsible to finalize PullAction based on type

+ tests for both v2 and v1 features
+ projectinfo v2 compatibility test
@MarcelGeo MarcelGeo changed the base branch from master to v2-pull-integration December 8, 2025 12:07
@MarcelGeo MarcelGeo changed the title Initial dirty version of v2 pull integration + porject info v2 Initial version of v2 pull integration + project info v2 Dec 8, 2025
Copy link
Contributor

@wonder-sk wonder-sk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's first do a bit of refactoring... the idea is that:

  • we introduce a new class that captures project delta (added/updated/updated_diff/deleted) files
  • the project delta class would be returned by get_pull_changes and get_push_changes
  • there would a function that can combine server changes (from get_pull_changes) and local changes (from get_push_changes) and return a list of tasks to do at the end of the pull (e.g. copy, copy conflict, apply diff, delete - like PullTask in mobile app's merginapi)
  • apply_pull_changes() would just go through the list of "pull tasks" and apply them, rather than again getting local changes and doing logic based on that

- models.py for some classes without specific location
- clenup of non-necssary functions from previous version
- refactor of pull handling using PullActions
+ descriptions
+ tests for appl_pull_actions
+ restore get_pull_changes for backward compatibility with project status
params = {"files_at_version": files_at_version}
resp = self.get(f"/v2/projects/{project_id}", params)
resp_json = json.load(resp)
project_workspace = resp_json.get("workspace", {})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can workspace be optional in response?

"""
min_version = "2025.8.2"
if not is_version_acceptable(self.server_version(), f"{min_version}"):
raise NotImplementedError(f"This needs server at version {min_version} or later")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we catch this in client http requests?

and remove the temporary files of the chunks
"""

def __init__(self, dest_file, downloaded_items, size_check=True):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a type for downloaded_items

self.size_check = size_check # whether we want to do merged file size check

def merge(self):
with open(self.dest_file, "wb") as final:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we need to create intermediate dirs?

resp = mc.get(
f"/v2/projects/{mp.project_id()}/raw/diff/{self.file_path}",
)
if resp.status in [200, 206]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always download diff in single request? If so, let's state it explicitly somewhere.

merge_files.extend(diff_merge_files)
basefiles_to_patch.append((item.path, [diff.id for diff in item.diffs]))

# let's check the base file existence
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dangling comment?

(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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why pull action type delete here and not also for other use cases?

self.update_without_rebase(path, src, dest, basefile, temp_dir)
for action in actions:
path = action.pull_delta_item.path
server = self.fpath(path, download_dir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe let's be explicit: server_file, local_file

shutil.copy(src, dest)
elif action_type == PullActionType.DELETE:
# remove local file
os.remove(live)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double check it still exist?

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.

4 participants