Skip to content

Commit 18b2dde

Browse files
Speedup of calc_release_version by minimizing subprocesses
Instead of continually asking Git whether a particular commit hash is pointed-to by a given tag, ask Git upfront for a mapping between all commit hashes and the associated tags. Then, look for the commit in the mapping as a single step.
1 parent 5d98be6 commit 18b2dde

File tree

1 file changed

+49
-28
lines changed

1 file changed

+49
-28
lines changed

build/calc_release_version.py

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -181,40 +181,62 @@ def get_next_minor(prerelease_marker: str):
181181
+ release_branch_match.group('brname') + '"')
182182
return version_str
183183

184-
def get_branch_tags(active_branch_name: str):
184+
def get_branch_tags(active_branch_name: str) -> list[str]:
185185
"""
186-
Returns the tag or tags (as a single string with newlines between tags)
187-
corresponding to the current branch, which must not be master. If the
188-
specified branch is a release branch then return all tags based on the
189-
major/minor X.Y release version. If the specified branch is neither master
190-
nor a release branch, then walk backwards in history until the first tag
191-
matching the glob '1.*' and return that tag.
186+
Returns a list of tags corresponding to the current branch, which must not
187+
be master. If the specified branch is a release branch then return all tags
188+
based on the major/minor X.Y release version. If the specified branch is
189+
neither master nor a release branch, then walk backwards in history until
190+
the first tag matching the glob '1.*' and return that tag.
192191
"""
193192

194193
if active_branch_name == 'master':
195194
raise Exception('this method is not meant to be called while on "master"')
196-
tags = ''
195+
197196
release_branch_match = RELEASE_BRANCH_RE.match(active_branch_name)
198197
if release_branch_match:
199198
# This is a release branch, so look for tags only on this branch
200199
tag_glob = release_branch_match.group('vermaj') + '.' \
201200
+ release_branch_match.group('vermin') + '.*'
202-
tags = check_output(['git', 'tag', '--list', tag_glob])
203-
else:
204-
# Not a release branch, so look for the most recent tag in history
205-
commits = check_output(['git', 'log', '--pretty=format:%H',
206-
'--no-merges'])
207-
if len(commits) > 0:
208-
for commit in commits.splitlines():
209-
tags = check_output(['git', 'tag', '--points-at',
210-
commit, '--list', '1.*'])
211-
if len(tags) > 0:
212-
# found a tag, we should be done
213-
break
214-
215-
return tags
216-
217-
def process_and_sort_tags(tags: str):
201+
return check_output(['git', 'tag', '--list', tag_glob]).splitlines()
202+
203+
# Not a release branch, so look for the most recent tag in history
204+
commits = check_output(['git', 'log', '--pretty=format:%H', '--no-merges'])
205+
tags_by_obj = get_object_tags()
206+
for commit in commits.splitlines():
207+
got = tags_by_obj.get(commit)
208+
if got:
209+
return got
210+
# No tags
211+
return []
212+
213+
214+
def iter_tag_lines():
215+
"""
216+
Generate a list of pairs of strings, where the first is a commit hash, and
217+
the second is a tag that is associated with that commit. Duplicate commits
218+
are possible.
219+
"""
220+
output = check_output(['git', 'tag', '--list', '1.*', '--format=%(*objectname)|%(tag)'])
221+
lines = output.splitlines()
222+
for l in lines:
223+
obj, tag = l.split('|', maxsplit=1)
224+
if tag:
225+
yield obj, tag
226+
227+
228+
def get_object_tags() -> dict[str, list[str]]:
229+
"""
230+
Obtain a mapping between commit hashes and a list of tags that point to
231+
that commit. Untagged commits will not be included in the resulting map.
232+
"""
233+
ret: dict[str, list[str]] = {}
234+
for obj, tag in iter_tag_lines():
235+
ret.setdefault(obj, []).append(tag)
236+
return ret
237+
238+
239+
def process_and_sort_tags(tags: list[str]):
218240
"""
219241
Given a string (as returned from get_branch_tags), return a sorted list of
220242
zero or more tags (sorted based on the Version comparison) which meet
@@ -228,15 +250,14 @@ def process_and_sort_tags(tags: str):
228250
if not tags or len(tags) == 0:
229251
return processed_and_sorted_tags
230252

231-
raw_tags = tags.splitlines()
232253
# find all the final release tags
233-
for tag in raw_tags:
254+
for tag in tags:
234255
release_tag_match = RELEASE_TAG_RE.match(tag)
235256
if release_tag_match and not release_tag_match.group('verpre'):
236257
processed_and_sorted_tags.append(tag)
237258
# collect together final release tags and pre-release tags for
238259
# versions that have not yet had a final release
239-
for tag in raw_tags:
260+
for tag in tags:
240261
tag_parts = tag.split('-')
241262
if len(tag_parts) >= 2 and tag_parts[0] not in processed_and_sorted_tags:
242263
processed_and_sorted_tags.append(tag)
@@ -318,7 +339,7 @@ def previous(rel_ver: str):
318339
version_parsed = parse_version(version_str)
319340
rel_ver_str = rel_ver
320341
rel_ver_parsed = parse_version(rel_ver_str)
321-
tags = check_output(['git', 'tag', '--list', '1.*'])
342+
tags = check_output(['git', 'tag', '--list', '1.*']).splitlines()
322343
processed_and_sorted_tags = process_and_sort_tags(tags)
323344
for tag in processed_and_sorted_tags:
324345
previous_tag_match = PREVIOUS_TAG_RE.match(tag)

0 commit comments

Comments
 (0)