From 459082f74a1e1d477d47f27c1f8e2e2cab098dd5 Mon Sep 17 00:00:00 2001 From: Jo Basevi Date: Wed, 18 Sep 2024 16:14:55 +1000 Subject: [PATCH] Add --start-point/-s argument to payu clone to specify what commit to start new branch from --- payu/branch.py | 16 ++++++++---- payu/subcommands/args.py | 11 ++++++++ payu/subcommands/clone_cmd.py | 7 +++--- test/test_branch.py | 47 +++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/payu/branch.py b/payu/branch.py index dd0c51b6..465dd7c5 100644 --- a/payu/branch.py +++ b/payu/branch.py @@ -212,6 +212,7 @@ def switch_symlink(lab_dir_path: Path, control_path: Path, def clone(repository: str, directory: Path, branch: Optional[str] = None, + start_point: Optional[str] = None, new_branch_name: Optional[str] = None, keep_uuid: bool = False, model_type: Optional[str] = None, @@ -227,11 +228,11 @@ def clone(repository: str, directory: Path The control directory where the repository will be cloned branch: Optional[str] - Name of branch to clone and checkout + Name of branch to checkout during the git clone + start_point: Optional[str] + Branch-name/commit/tag to start new branch from new_branch_name: Optional[str] - Name of new branch to create and checkout. - If branch is also defined, the new branch will start from the - latest commit of the branch. + Name of new branch to create and checkout keep_uuid: bool, default False Keep UUID unchanged, if it exists config_path: Optional[Path] @@ -248,6 +249,10 @@ def clone(repository: str, Parent experiment UUID to add to generated metadata Returns: None + + Note: branch, if defined, can be set to avoid initially checking out the + default branch during git clone - this is useful for repositories where + the default branch is not a payu configuration. """ # Resolve directory to an absolute path control_path = directory.resolve() @@ -278,7 +283,8 @@ def clone(repository: str, control_path=control_path, model_type=model_type, lab_path=lab_path, - parent_experiment=parent_experiment) + parent_experiment=parent_experiment, + start_point=start_point) else: # Checkout branch if branch is None: diff --git a/payu/subcommands/args.py b/payu/subcommands/args.py index bb2ec3d6..178de84b 100644 --- a/payu/subcommands/args.py +++ b/payu/subcommands/args.py @@ -191,6 +191,17 @@ } } +# Clone branch +clone_start_point = { + 'flags': ('--start-point', '-s'), + 'parameters': { + 'action': 'store', + 'dest': 'start_point', + 'default': None, + 'help': 'New branch will start from this commit or tag' + } +} + # Clone create branch new_branch_name = { 'flags': ('--new-branch', '-b'), diff --git a/payu/subcommands/clone_cmd.py b/payu/subcommands/clone_cmd.py index c15c60a0..1616593c 100644 --- a/payu/subcommands/clone_cmd.py +++ b/payu/subcommands/clone_cmd.py @@ -18,7 +18,7 @@ args.keep_uuid, args.clone_branch, args.repository, args.local_directory, args.new_branch_name, args.restart_path, - args.parent_experiment] + args.parent_experiment, args.clone_start_point] def transform_strings_to_path(path_str=None): @@ -27,7 +27,7 @@ def transform_strings_to_path(path_str=None): def runcmd(model_type, config_path, lab_path, keep_uuid, branch, repository, local_directory, new_branch_name, restart_path, - parent_experiment): + parent_experiment, start_point): """Execute the command.""" config_path = transform_strings_to_path(config_path) restart_path = transform_strings_to_path(restart_path) @@ -43,7 +43,8 @@ def runcmd(model_type, config_path, lab_path, keep_uuid, lab_path=lab_path, new_branch_name=new_branch_name, restart_path=restart_path, - parent_experiment=parent_experiment) + parent_experiment=parent_experiment, + start_point=start_point) runscript = runcmd diff --git a/test/test_branch.py b/test/test_branch.py index f8fc9ae7..a10806e1 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -536,6 +536,53 @@ def test_clone(mock_uuid): assert [head.name for head in cloned_repo2.heads] == ["Branch1", "Branch2"] +@pytest.mark.parametrize( + "start_point_type", ["commit", "tag"] +) +def test_clone_startpoint(start_point_type): + # Create a repo to clone + source_repo_path = tmpdir / "sourceRepo" + source_repo_path.mkdir() + source_repo = setup_control_repository(path=source_repo_path) + + # Create branch1 + branch1 = source_repo.create_head("Branch1") + branch1_commit = branch1.object.hexsha + if start_point_type == "tag": + source_repo.create_tag('v1.0', ref=branch1.commit) + start_point = 'v1.0' + elif start_point_type == "commit": + start_point = branch1_commit + + # Add another commit on main branch so the commit is different to branch1 + (source_repo_path / "mock_file.txt").touch() + source_repo.index.add("mock_file.txt") + source_repo.index.commit("Another commit with a mock file") + + source_repo_commit = source_repo.active_branch.object.hexsha + assert source_repo_commit != branch1_commit + + # Run Clone + cloned_repo_path = tmpdir / "clonedRepo" + with cd(tmpdir): + clone( + repository=str(source_repo_path), + directory=cloned_repo_path, + lab_path=labdir, + new_branch_name="Branch3", + start_point=start_point + ) + + cloned_repo = git.Repo(cloned_repo_path) + + # Check branched starting from start point + second_latest_commit = list(cloned_repo.iter_commits(max_count=2))[1] + assert second_latest_commit.hexsha == branch1_commit + + # Latest commit is different (new commit from metadata) + assert source_repo_commit != cloned_repo.active_branch.object.hexsha + + def add_and_commit_metadata(repo, metadata): """Helper function to create/update metadata file and commit""" metadata_path = ctrldir / "metadata.yaml"