From 7a0ee69ea84d16b5b48d20a45d0b19c3abab327b Mon Sep 17 00:00:00 2001 From: James Gerity Date: Fri, 15 Dec 2023 23:47:15 -0500 Subject: [PATCH] version: retrieve commit hash for git worktrees --- sopel/builtins/version.py | 40 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/sopel/builtins/version.py b/sopel/builtins/version.py index 536eaa83fd..1e54065b4c 100644 --- a/sopel/builtins/version.py +++ b/sopel/builtins/version.py @@ -19,12 +19,14 @@ GIT_DIR = os.path.join(PROJECT_DIR, '.git') -def git_info(): - head = os.path.join(GIT_DIR, 'HEAD') +def _read_commit(gitdir: str, head: str) -> str | None: + """ + Given paths to ``.git/`` and ``HEAD``, determine the associated commit hash + """ if os.path.isfile(head): with open(head) as h: head_loc = h.readline()[5:-1] # strip ref: and \n - head_file = os.path.join(GIT_DIR, head_loc) + head_file = os.path.join(gitdir, head_loc) if os.path.isfile(head_file): with open(head_file) as h: sha = h.readline() @@ -32,6 +34,38 @@ def git_info(): return sha +def _resolve_git_dirs(pth: str) -> tuple[str, str]: + """ + Resolve a ``.git`` path to its 'true' ``.git/`` and `HEAD` + + This helper is useful for dealing with the ``.git`` file stored in a + git worktree. + """ + # default to the old behavior: assume `pth` is a valid .git/ to begin with + gitdir = pth + head = os.path.join(pth, "HEAD") + + if os.path.isfile(pth): + # this may be a worktree, let's override the result properly if so + with open(pth, 'r') as f: + first, rest = next(f).strip().split(maxsplit=1) + if first == "gitdir:": + # line is "gitdir: /path/to/parentrepo/.git/worktrees/thispath" + gitdir = os.path.dirname(os.path.dirname(rest)) + head = os.path.join(rest, "HEAD") + # else: we can't make sense of this file, stick to the default + + return gitdir, head + + +def git_info() -> str | None: + """ + Determine the git commit hash of this Sopel, if applicable + """ + gitdir, head = _resolve_git_dirs(GIT_DIR) + return _read_commit(gitdir, head) + + @plugin.command('version') @plugin.example('.version [plugin_name]') @plugin.output_prefix('[version] ')