Fix finding the kernel version that a commit is based on

For a commit based on a release candidate version like v6.4-rc1, make
sure to return v6.3, not v6.4.

Signed-off-by: Eric Biggers <ebiggers@google.com>
diff --git a/stable_utils.py b/stable_utils.py
index 5be0518..356bc1d 100755
--- a/stable_utils.py
+++ b/stable_utils.py
@@ -428,6 +428,36 @@
         """Returns a list of commit IDs that have the given normalized title."""
         return self._dict.get(normalized_title, [])
 
+def _extract_kernel_version(commit):
+    major = -1
+    minor = -1
+    extraversion = ''
+    for line in git(['show', f'{commit}:Makefile']).split('\n'):
+        if line.startswith('VERSION = '):
+            major = int(line.split()[2])
+        if line.startswith('PATCHLEVEL = '):
+            minor = int(line.split()[2])
+        try:
+            if line.startswith('EXTRAVERSION = '):
+                extraversion = line.split()[2]
+        except IndexError:
+            pass
+    if major < 0 or minor < 0:
+        error(f'Failed to extract kernel major.minor version number at {commit}')
+    return (major, minor, extraversion)
+
+def extract_kernel_version(commit):
+    """Returns the last v{major}.{minor} tag that the given kernel commit is
+    based on.  Release candidates aren't counted, so if for example the commit
+    is based on v6.4-rc1, this returns v6.3."""
+    (major, minor, extraversion) = _extract_kernel_version(commit)
+    if 'rc' in extraversion:
+        commit = f'v{major}.{minor}-rc1~1'
+        (major, minor, extraversion) = _extract_kernel_version(commit)
+        if extraversion:
+            error(f'Unexpectedly found EXTRAVERSION at {commit}')
+    return f'v{major}.{minor}'
+
 def get_history_index(end_commit):
     """Returns a GitHistoryIndex that indexes the history of the Linux kernel by
     commit title in the range config.start_of_history to end_commit.
@@ -437,18 +467,7 @@
     the history until end_commit is then generated by loading the cached index
     and appending the commits from major.minor to end_commit."""
 
-    major = -1
-    minor = -1
-    for line in git(['show', f'{end_commit}:Makefile']).split('\n'):
-        if line.startswith('VERSION = '):
-            major = int(line.split()[2])
-        if line.startswith('PATCHLEVEL = '):
-            minor = int(line.split()[2])
-        if major >= 0 and minor >= 0:
-            break
-    if major < 0 or minor < 0:
-        error(f'Failed to extract kernel major.minor version number at {end_commit}')
-    baseline = f'v{major}.{minor}'
+    baseline = extract_kernel_version(end_commit)
     histfile = f'history_{config.start_of_history}..{baseline}'
     try:
         content = git_cache.read(histfile)
diff --git a/tests/test-extract-kernel-version.py b/tests/test-extract-kernel-version.py
new file mode 100644
index 0000000..13ba224
--- /dev/null
+++ b/tests/test-extract-kernel-version.py
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright 2023 Google LLC
+
+from stable_utils import *
+
+TEST_CASES = [
+    ('v6.1', 'v6.1'),
+    ('v6.1.23', 'v6.1'),
+    ('v6.3-rc7', 'v6.2'),
+    ('v6.3', 'v6.3'),
+    ('v6.4-rc1', 'v6.3'),
+]
+
+def assert_equals(expected, actual):
+    if expected != actual:
+        raise AssertionError(f"Expected '{expected}' but was '{actual}'")
+
+for case in TEST_CASES:
+    assert_equals(case[1], extract_kernel_version(case[0]))