diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index a63feff0a..e5f51b4b8 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -77,42 +77,60 @@ def _bfs_for_latest_version_in_history( # Step 3. Latest full release version within the history of the current branch # Breadth-first search the merge-base and its parent commits for one which matches # the tag of the latest full release tag in history - def bfs(visited: set[Commit], q: Queue[Commit]) -> Version | None: - if q.empty(): - log.debug("queue is empty, returning none") - return None + def bfs(start_commit: Commit | TagObject | Blob | Tree) -> Version | None: + # Derived from Geeks for Geeks + # https://www.geeksforgeeks.org/python-program-for-breadth-first-search-or-bfs-for-a-graph/?ref=lbp - node = q.get() + # Create a queue for BFS + q: Queue[Commit | TagObject | Blob | Tree] = Queue() - log.debug("checking if commit %s matches any tags", node.hexsha) - version = tag_sha_2_version_lookup.get(node.hexsha, None) + # Create a set to store visited graph nodes (commit objects in this case) + visited: set[Commit | TagObject | Blob | Tree] = set() - if version is not None: - log.info( - "found latest version in branch history: %r (%s)", - str(version), - node.hexsha[:7], - ) - return version + # Add the source node in the queue & mark as visited to start the search + q.put(start_commit) + visited.add(start_commit) + + # Initialize the result to None + result = None + + # Traverse the git history until it finds a version tag if one exists + while not q.empty(): + node = q.get() + visited.add(node) + + log.debug("checking if commit %s matches any tags", node.hexsha) + version = tag_sha_2_version_lookup.get(node.hexsha, None) - log.debug("commit %s doesn't match any tags", node.hexsha) + if version is not None: + log.info( + "found latest version in branch history: %r (%s)", + str(version), + node.hexsha[:7], + ) + result = version + break - visited.add(node) + log.debug("commit %s doesn't match any tags", node.hexsha) - for parent in node.parents: - if parent in visited: - log.debug("parent commit %s already visited", node.hexsha) - continue - log.debug("queuing parent commit %s", parent.hexsha) - q.put(parent) + # Add all parent commits to the queue if they haven't been visited + for parent in node.parents: + if parent in visited: + log.debug("parent commit %s already visited", node.hexsha) + continue - return bfs(visited, q) + log.debug("queuing parent commit %s", parent.hexsha) + q.put(parent) - q: Queue[Commit] = Queue() - q.put(merge_base) - latest_version = bfs(set(), q) + return result + + # Run a Breadth First Search to find the latest version in the history + latest_version = bfs(merge_base) + if latest_version is not None: + log.info("the latest version in this branch's history is %s", latest_version) + else: + log.info("no version tags found in this branch's history") - log.info("the latest version in this branch's history is %s", latest_version) return latest_version