From f872a12400bada92da5c52b61b79dbada91e0554 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi Date: Sun, 4 May 2025 02:48:30 +0000 Subject: [PATCH 1/3] Add a test --- cherry_picker/test_cherry_picker.py | 54 ++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/cherry_picker/test_cherry_picker.py b/cherry_picker/test_cherry_picker.py index 5eefb1f..28ff3d5 100644 --- a/cherry_picker/test_cherry_picker.py +++ b/cherry_picker/test_cherry_picker.py @@ -1165,6 +1165,58 @@ def test_backport_success( assert get_state() == WORKFLOW_STATES.UNSET +def test_backport_pause_aud_continue_multiple( + tmp_git_repo_dir, git_branch, git_add, git_commit, git_checkout +): + cherry_pick_target_branches = ("3.8", "3.7", "3.6") + pr_remote = "origin" + upstream_remote = "upstream" + test_file = "some.file" + tmp_git_repo_dir.join(test_file).write("some contents") + for branch in cherry_pick_target_branches: + git_branch(branch) + git_branch(f"{upstream_remote}/{branch}", branch) + git_add(test_file) + git_commit("Add a test file") + scm_revision = get_sha1_from("HEAD") + + with mock.patch("cherry_picker.cherry_picker.validate_sha", return_value=True): + cherry_picker = CherryPicker( + pr_remote, scm_revision, cherry_pick_target_branches, push=False + ) + + cherry_picker._upstream = upstream_remote + with ( + mock.patch.object(cherry_picker, "push_to_remote"), + mock.patch.object(cherry_picker, "fetch_upstream"), + mock.patch.object( + cherry_picker, "amend_commit_message", return_value="commit message" + ), + ): + cherry_picker.backport() + + assert get_state() == WORKFLOW_STATES.BACKPORT_PAUSED + assert cherry_picker.get_remaining_backports() == ["3.7", "3.6"] + + with mock.patch("cherry_picker.cherry_picker.validate_sha", return_value=True): + cherry_picker = CherryPicker( + pr_remote, scm_revision, cherry_pick_target_branches + ) + + cherry_picker._upstream = upstream_remote + with ( + mock.patch("cherry_picker.cherry_picker.wipe_cfg_vals_from_git_cfg"), + mock.patch.object(cherry_picker, "push_to_remote"), + mock.patch.object(cherry_picker, "fetch_upstream"), + mock.patch.object( + cherry_picker, "amend_commit_message", return_value="commit message" + ), + ): + cherry_picker.continue_cherry_pick() + assert get_state() == WORKFLOW_STATES.BACKPORT_LOOP_END + assert cherry_picker.get_remaining_backports() == [] + + @pytest.mark.parametrize("already_committed", (True, False)) @pytest.mark.parametrize("push", (True, False)) def test_backport_pause_and_continue( @@ -1255,7 +1307,7 @@ def test_backport_pause_and_continue( amend_commit_message.assert_not_called() if push: - assert get_state() == WORKFLOW_STATES.BACKPORTING_CONTINUATION_SUCCEED + assert get_state() == WORKFLOW_STATES.BACKPORT_LOOPING else: assert get_state() == WORKFLOW_STATES.BACKPORT_PAUSED From 8ca4dbca3c203311ad2d75e658a062e8d4157719 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi Date: Sun, 4 May 2025 02:49:17 +0000 Subject: [PATCH 2/3] Implement saving remaining backport branches to gitconfig --- cherry_picker/cherry_picker.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/cherry_picker/cherry_picker.py b/cherry_picker/cherry_picker.py index 52df11d..e4401ed 100755 --- a/cherry_picker/cherry_picker.py +++ b/cherry_picker/cherry_picker.py @@ -165,6 +165,24 @@ def remember_previous_branch(self): current_branch = get_current_branch() save_cfg_vals_to_git_cfg(previous_branch=current_branch) + def set_remaining_backports(self, branches): + """Save the remaining backport branches into Git config.""" + save_cfg_vals_to_git_cfg(remaining_backport=" ".join(branches)) + + def get_remaining_backports(self): + """Get the remaining backport branches from Git config.""" + branches = load_val_from_git_cfg("remaining_backport") + if branches is None: + return [] + return branches.split() + + def pop_remaining_backports(self): + """Get one of the remaining backport branch and remove it.""" + branches = self.get_remaining_backports() + branch = branches.pop(0) + self.set_remaining_backports(branches) + return branch + @property def upstream(self): """Get the remote name to use for upstream branches @@ -535,9 +553,14 @@ def backport(self): set_state(WORKFLOW_STATES.BACKPORT_STARTING) self.fetch_upstream() self.remember_previous_branch() + self.set_remaining_backports(self.sorted_branches) + self.process_remaining_backports() + + def process_remaining_backports(self): set_state(WORKFLOW_STATES.BACKPORT_LOOPING) - for maint_branch in self.sorted_branches: + while self.get_remaining_backports(): + maint_branch = self.pop_remaining_backports() set_state(WORKFLOW_STATES.BACKPORT_LOOP_START) click.echo(f"Now backporting '{self.commit_sha1}' into '{maint_branch}'") @@ -577,6 +600,7 @@ def backport(self): return # to preserve the correct state set_state(WORKFLOW_STATES.BACKPORT_LOOP_END) reset_stored_previous_branch() + reset_stored_config_ref() reset_state() def abort_cherry_pick(self): @@ -669,6 +693,8 @@ def continue_cherry_pick(self): self.pause_after_committing(cherry_pick_branch) return # to preserve the correct state + self.process_remaining_backports() + else: click.echo( f"Current branch ({cherry_pick_branch}) is not a backport branch. " @@ -676,9 +702,6 @@ def continue_cherry_pick(self): ) set_state(WORKFLOW_STATES.CONTINUATION_FAILED) - reset_stored_previous_branch() - reset_stored_config_ref() - reset_state() def check_repo(self): """ From def06e4cea65bef8500f8ca4e14e6c584a3e5011 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 01:51:07 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cherry_picker/cherry_picker.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cherry_picker/cherry_picker.py b/cherry_picker/cherry_picker.py index e4401ed..2dcd1ec 100755 --- a/cherry_picker/cherry_picker.py +++ b/cherry_picker/cherry_picker.py @@ -556,7 +556,6 @@ def backport(self): self.set_remaining_backports(self.sorted_branches) self.process_remaining_backports() - def process_remaining_backports(self): set_state(WORKFLOW_STATES.BACKPORT_LOOPING) while self.get_remaining_backports(): @@ -702,7 +701,6 @@ def continue_cherry_pick(self): ) set_state(WORKFLOW_STATES.CONTINUATION_FAILED) - def check_repo(self): """ Check that the repository is for the project we're configured to operate on.