I have a repository with a version (semantic versioning) hardcoded in one of its files. Each time a developer adds a change (let's call it a type 1 change), I need to review those changes and increment the minor version component. If a developer adds a type 2 change, I increment the patch version component. I have set up a convention for developers: if they are adding a type 1 change, they must create a branch and name it A/ID. If they are adding a type 2 change, they must create a branch and name it bugfix/A/ID. Additionally, I wrote some Git hook (commit-msg, since it will be called in both git commit and git merge) script that, based on the branch being merged into the default branch, automatically increment the related version components. Below is the Git hook script I wrote: #!/usr/bin/env python3 import subprocess import sys import pathlib import re def main(): default, base = "main", "package/__init__.py" head = pathlib.Path(".git/MERGE_HEAD") if not head.exists(): exit(0) with head.open() as f: commit = f.read().strip() command = ["git", "name-rev", "--name-only", commit] process = subprocess.run(command, capture_output=True, encoding="UTF-8") if process.returncode != 0: print( f"Failing to perform `commit-msg` commit operations. The `{' '.join(command)}` command and its logs may be helpful.", " ".join(process.args), process.stderr, sep="\n", file=sys.stderr, ) exit(process.returncode) merge = process.stdout.strip() command = ["git", "rev-parse", "--abbrev-ref", "HEAD"] process = subprocess.run(command, capture_output=True, encoding="UTF-8") if process.returncode != 0: print( f"Failing to perform `commit-msg` commit operations. The `{' '.join(command)}` command and its logs may be helpful.", " ".join(process.args), process.stderr, sep="\n", file=sys.stderr, ) exit(process.returncode) current = process.stdout.strip() if current != default or ( not merge.startswith("A/") and not merge.startswith("bugfix/A/") ): exit(0) semver = re.compile( r"(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?" ) version = {"major": 0, "minor": 0, "patch": 0} with open(base, mode="r", encoding="UTF-8") as f: line, match = f.readline(), None while line: if line.startswith("__version__"): match = re.match(r'__version__.*"(?P<version>.*?)"', line) break line = f.readline() if match == None: print( "Failing to perform `commit-msg` commit operations. The Semantic Versioning regular expression cannot find the detection version in the Python module!", file=sys.stderr, ) exit(1) match = semver.match(match.group("version")) if match == None: print( "Failing to perform `commit-msg` commit operations. The detection version format in this repository does not match the Semantic Versioning format.", file=sys.stderr, ) exit(1) version["major"], version["minor"], version["patch"] = ( int(match.group("major")), int(match.group("minor")), int(match.group("patch")), ) if merge.startswith("A/"): version["minor"] += 1 elif merge.startswith("bugfix/A/"): version["patch"] += 1 with open(base, mode="r", encoding="UTF-8") as f: module = f.read() with open(base, mode="w", encoding="UTF-8") as f: module = re.sub( r'__version__ = "(?P<version>.*?)"', f'__version__ = "{version["major"]}.{version["minor"]}.{version["patch"]}-rc"', module, ) f.write(module) command = ["git", "add", base] process = subprocess.run(command, capture_output=True, encoding="UTF-8") if process.returncode != 0: print( f'Failing to perform `commit-msg` commit operations. The `{" ".join(command)}` command and its logs may be helpful.', " ".join(process.args), process.stderr, sep="\n", file=sys.stderr, ) exit(process.returncode) print("Upgraded.") if __name__ == "__main__": main() The script works as expected, with one exception: at the end of the script, you can see I'm using git add <FILE> to include the changes made earlier to the version hardcoded in the Python module. This command works as expected and adds the changes to the Git index. However, after the hook finishes and the merge commit message appears, when I accept the message, the changes are not included in the merge commit and are left in the index alone! What am I doing wrong? I'm online and active, answering questions as they appear. The solution to the former problem that I described comes first, but suggestions are welcome on every aspect of the problem, including changes to the overall approach, Python code, Git hook name, and philosophy. Continue reading...