Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 940787a

Browse filesBrowse files
Jackenmenezio-melottiMariatta
authored
Allow passing a base branch that doesn't have version info (#70)
Co-authored-by: Ezio Melotti <ezio.melotti@gmail.com> Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> Co-authored-by: Mariatta Wijaya <Mariatta@users.noreply.github.com>
1 parent a1552fb commit 940787a
Copy full SHA for 940787a

File tree

Expand file treeCollapse file tree

3 files changed

+100
-41
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+100
-41
lines changed

‎README.md

Copy file name to clipboardExpand all lines: README.md
+27-22Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -137,33 +137,38 @@ repo = "aiohttp"
137137
check_sha = "f382b5ffc445e45a110734f5396728da7914aeb6"
138138
fix_commit_msg = false
139139
default_branch = "devel"
140+
require_version_in_branch_name = false
140141
```
141142

142143
Available config options:
143144

144145
```
145-
team github organization or individual nick,
146-
e.g "aio-libs" for https://github.com/aio-libs/aiohttp
147-
("python" by default)
148-
149-
repo github project name,
150-
e.g "aiohttp" for https://github.com/aio-libs/aiohttp
151-
("cpython" by default)
152-
153-
check_sha A long hash for any commit from the repo,
154-
e.g. a sha1 hash from the very first initial commit
155-
("7f777ed95a19224294949e1b4ce56bbffcb1fe9f" by default)
156-
157-
fix_commit_msg Replace # with GH- in cherry-picked commit message.
158-
It is the default behavior for CPython because of external
159-
Roundup bug tracker (https://bugs.python.org) behavior:
160-
#xxxx should point on issue xxxx but GH-xxxx points
161-
on pull-request xxxx.
162-
For projects using GitHub Issues, this option can be disabled.
163-
164-
default_branch Project's default branch name,
165-
e.g "devel" for https://github.com/ansible/ansible
166-
("main" by default)
146+
team github organization or individual nick,
147+
e.g "aio-libs" for https://github.com/aio-libs/aiohttp
148+
("python" by default)
149+
150+
repo github project name,
151+
e.g "aiohttp" for https://github.com/aio-libs/aiohttp
152+
("cpython" by default)
153+
154+
check_sha A long hash for any commit from the repo,
155+
e.g. a sha1 hash from the very first initial commit
156+
("7f777ed95a19224294949e1b4ce56bbffcb1fe9f" by default)
157+
158+
fix_commit_msg Replace # with GH- in cherry-picked commit message.
159+
It is the default behavior for CPython because of external
160+
Roundup bug tracker (https://bugs.python.org) behavior:
161+
#xxxx should point on issue xxxx but GH-xxxx points
162+
on pull-request xxxx.
163+
For projects using GitHub Issues, this option can be disabled.
164+
165+
default_branch Project's default branch name,
166+
e.g "devel" for https://github.com/ansible/ansible
167+
("main" by default)
168+
169+
require_version_in_branch_name Allow backporting to branches whose names don't contain
170+
something that resembles a version number
171+
(i.e. at least two dot-separated numbers).
167172
```
168173

169174
To customize the tool for used by other project:

‎cherry_picker/cherry_picker.py

Copy file name to clipboardExpand all lines: cherry_picker/cherry_picker.py
+30-9Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import collections
66
import enum
7+
import functools
78
import os
89
import re
910
import subprocess
@@ -31,6 +32,7 @@
3132
"check_sha": "7f777ed95a19224294949e1b4ce56bbffcb1fe9f",
3233
"fix_commit_msg": True,
3334
"default_branch": "main",
35+
"require_version_in_branch_name": True,
3436
}
3537
)
3638

@@ -191,7 +193,9 @@ def upstream(self):
191193
@property
192194
def sorted_branches(self):
193195
"""Return the branches to cherry-pick to, sorted by version."""
194-
return sorted(self.branches, reverse=True, key=version_from_branch)
196+
return sorted(
197+
self.branches, key=functools.partial(compute_version_sort_key, self.config)
198+
)
195199

196200
@property
197201
def username(self):
@@ -333,7 +337,7 @@ def get_updated_commit_message(self, cherry_pick_branch):
333337
updated_commit_message = self.get_commit_message(self.commit_sha1)
334338
if self.prefix_commit:
335339
updated_commit_message = remove_commit_prefix(updated_commit_message)
336-
base_branch = get_base_branch(cherry_pick_branch)
340+
base_branch = get_base_branch(cherry_pick_branch, config=self.config)
337341
updated_commit_message = f"[{base_branch}] {updated_commit_message}"
338342

339343
# Add '(cherry picked from commit ...)' to the message
@@ -600,7 +604,7 @@ def continue_cherry_pick(self):
600604
if cherry_pick_branch.startswith("backport-"):
601605
set_state(WORKFLOW_STATES.CONTINUATION_STARTED)
602606
# amend the commit message, prefix with [X.Y]
603-
base = get_base_branch(cherry_pick_branch)
607+
base = get_base_branch(cherry_pick_branch, config=self.config)
604608
short_sha = cherry_pick_branch[
605609
cherry_pick_branch.index("-") + 1 : cherry_pick_branch.index(base) - 1
606610
]
@@ -831,7 +835,7 @@ def cherry_pick_cli(
831835
sys.exit(-1)
832836

833837

834-
def get_base_branch(cherry_pick_branch):
838+
def get_base_branch(cherry_pick_branch, *, config):
835839
"""
836840
return '2.7' from 'backport-sha-2.7'
837841
@@ -855,7 +859,7 @@ def get_base_branch(cherry_pick_branch):
855859

856860
# Subject the parsed base_branch to the same tests as when we generated it
857861
# This throws a ValueError if the base_branch doesn't meet our requirements
858-
version_from_branch(base_branch)
862+
compute_version_sort_key(config, base_branch)
859863

860864
return base_branch
861865

@@ -876,14 +880,31 @@ def validate_sha(sha):
876880
)
877881

878882

879-
def version_from_branch(branch):
883+
def compute_version_sort_key(config, branch):
880884
"""
881-
return version information from a git branch name
885+
Get sort key based on version information from the given git branch name.
886+
887+
This function can be used as a sort key in list.sort()/sorted() provided that
888+
you additionally pass config as a first argument by e.g. wrapping it with
889+
functools.partial().
890+
891+
Branches with version information come first and are sorted from latest
892+
to oldest version.
893+
Branches without version information come second and are sorted alphabetically.
882894
"""
883895
m = re.search(r"\d+(?:\.\d+)+", branch)
884-
if not m:
896+
if m:
897+
raw_version = m[0].split(".")
898+
# Use 0 to sort version numbers *before* regular branch names
899+
return (0, *(-int(x) for x in raw_version))
900+
901+
if not branch:
902+
raise ValueError("Branch name is an empty string.")
903+
if config["require_version_in_branch_name"]:
885904
raise ValueError(f"Branch {branch} seems to not have a version in its name.")
886-
return tuple(map(int, m[0].split(".")))
905+
906+
# Use 1 to sort regular branch names *after* version numbers
907+
return (1, branch)
887908

888909

889910
def get_current_branch():

‎cherry_picker/test_cherry_picker.py

Copy file name to clipboardExpand all lines: cherry_picker/test_cherry_picker.py
+43-10Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,20 +149,20 @@ def tmp_git_repo_dir(tmpdir, cd, git_init, git_commit, git_config):
149149

150150

151151
@mock.patch("subprocess.check_output")
152-
def test_get_base_branch(subprocess_check_output):
152+
def test_get_base_branch(subprocess_check_output, config):
153153
# The format of cherry-pick branches we create are::
154154
# backport-{SHA}-{base_branch}
155155
subprocess_check_output.return_value = b"22a594a0047d7706537ff2ac676cdc0f1dcb329c"
156156
cherry_pick_branch = "backport-22a594a-2.7"
157-
result = get_base_branch(cherry_pick_branch)
157+
result = get_base_branch(cherry_pick_branch, config=config)
158158
assert result == "2.7"
159159

160160

161161
@mock.patch("subprocess.check_output")
162-
def test_get_base_branch_which_has_dashes(subprocess_check_output):
162+
def test_get_base_branch_which_has_dashes(subprocess_check_output, config):
163163
subprocess_check_output.return_value = b"22a594a0047d7706537ff2ac676cdc0f1dcb329c"
164164
cherry_pick_branch = "backport-22a594a-baseprefix-2.7-basesuffix"
165-
result = get_base_branch(cherry_pick_branch)
165+
result = get_base_branch(cherry_pick_branch, config=config)
166166
assert result == "baseprefix-2.7-basesuffix"
167167

168168

@@ -171,14 +171,14 @@ def test_get_base_branch_which_has_dashes(subprocess_check_output):
171171
[
172172
"backport-22a594a", # Not enough fields
173173
"prefix-22a594a-2.7", # Not the prefix we were expecting
174-
"backport-22a594a-base", # No version info in the base branch
174+
"backport-22a594a-", # No base branch
175175
],
176176
)
177177
@mock.patch("subprocess.check_output")
178-
def test_get_base_branch_invalid(subprocess_check_output, cherry_pick_branch):
178+
def test_get_base_branch_invalid(subprocess_check_output, cherry_pick_branch, config):
179179
subprocess_check_output.return_value = b"22a594a0047d7706537ff2ac676cdc0f1dcb329c"
180180
with pytest.raises(ValueError):
181-
get_base_branch(cherry_pick_branch)
181+
get_base_branch(cherry_pick_branch, config=config)
182182

183183

184184
@mock.patch("subprocess.check_output")
@@ -206,18 +206,33 @@ def test_get_author_info_from_short_sha(subprocess_check_output):
206206

207207

208208
@pytest.mark.parametrize(
209-
"input_branches,sorted_branches",
209+
"input_branches,sorted_branches,require_version",
210210
[
211-
(["3.1", "2.7", "3.10", "3.6"], ["3.10", "3.6", "3.1", "2.7"]),
211+
(["3.1", "2.7", "3.10", "3.6"], ["3.10", "3.6", "3.1", "2.7"], True),
212212
(
213213
["stable-3.1", "lts-2.7", "3.10-other", "smth3.6else"],
214214
["3.10-other", "smth3.6else", "stable-3.1", "lts-2.7"],
215+
True,
216+
),
217+
(["3.1", "2.7", "3.10", "3.6"], ["3.10", "3.6", "3.1", "2.7"], False),
218+
(
219+
["stable-3.1", "lts-2.7", "3.10-other", "smth3.6else"],
220+
["3.10-other", "smth3.6else", "stable-3.1", "lts-2.7"],
221+
False,
222+
),
223+
(
224+
["3.7", "3.10", "2.7", "foo", "stable", "branch"],
225+
["3.10", "3.7", "2.7", "branch", "foo", "stable"],
226+
False,
215227
),
216228
],
217229
)
218230
@mock.patch("os.path.exists")
219-
def test_sorted_branch(os_path_exists, config, input_branches, sorted_branches):
231+
def test_sorted_branch(
232+
os_path_exists, config, input_branches, sorted_branches, require_version
233+
):
220234
os_path_exists.return_value = True
235+
config["require_version_in_branch_name"] = require_version
221236
cp = CherryPicker(
222237
"origin",
223238
"22a594a0047d7706537ff2ac676cdc0f1dcb329c",
@@ -227,6 +242,21 @@ def test_sorted_branch(os_path_exists, config, input_branches, sorted_branches):
227242
assert cp.sorted_branches == sorted_branches
228243

229244

245+
@mock.patch("os.path.exists")
246+
def test_invalid_branch_empty_string(os_path_exists, config):
247+
os_path_exists.return_value = True
248+
# already tested for require_version_in_branch_name=True below
249+
config["require_version_in_branch_name"] = False
250+
cp = CherryPicker(
251+
"origin",
252+
"22a594a0047d7706537ff2ac676cdc0f1dcb329c",
253+
["3.1", "2.7", "3.10", "3.6", ""],
254+
config=config,
255+
)
256+
with pytest.raises(ValueError, match=r"^Branch name is an empty string\.$"):
257+
cp.sorted_branches
258+
259+
230260
@pytest.mark.parametrize(
231261
"input_branches",
232262
[
@@ -460,6 +490,7 @@ def test_load_full_config(tmp_git_repo_dir, git_add, git_commit):
460490
"team": "python",
461491
"fix_commit_msg": True,
462492
"default_branch": "devel",
493+
"require_version_in_branch_name": True,
463494
},
464495
)
465496

@@ -483,6 +514,7 @@ def test_load_partial_config(tmp_git_repo_dir, git_add, git_commit):
483514
"team": "python",
484515
"fix_commit_msg": True,
485516
"default_branch": "main",
517+
"require_version_in_branch_name": True,
486518
},
487519
)
488520

@@ -511,6 +543,7 @@ def test_load_config_no_head_sha(tmp_git_repo_dir, git_add, git_commit):
511543
"team": "python",
512544
"fix_commit_msg": True,
513545
"default_branch": "devel",
546+
"require_version_in_branch_name": True,
514547
},
515548
)
516549

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.