How to include the current Git commit ID and branch in my document?
As stated in the question, the branch name can be extracted from .git/HEAD
and given [branch name]
, the commit ID can be found in .git/refs/heads/[branch name]
.
The package catchfile
provides the command \CatchFileDef
, which allows us to read .git/HEAD
into a macro. As HEAD
has no file extension, MiKTeX users have to add a trailing dot to the file name:
\CatchFileDef{\headfull}{.git/HEAD.}{}
This assigns something like ref: refs/heads/master
to \headfull
. As this string has a trailing whitespace character, we use \StrGobbleRight
from the xstring package to trim it:1
\StrGobbleRight{\headfull}{1}[\head]
In order to extract only the branch name (master
in the example) from this string, we can use \StrBehind
:
\StrBehind[2]{\head}{/}[\branch]
This saves the branch name in \branch
. Finally, we can use \CatchFileDef
again, to save the commit ID in \commit
:
\CatchFileDef{\commit}{.git/refs/heads/\branch.}{}
There are some edge cases where .git/refs/heads/\branch.
does not exist: After running git pack-refs
(which is a side effect of git gc --aggressive
, for example), the heads are packed into the file .git/packed-refs
instead of individual branchname
files. As a workaround, check if the file exists before trying to read it:
\IfFileExists{.git/refs/heads/\branch.}{%
\CatchFileDef{\commit}{.git/refs/heads/\branch.}{}}{%
\newcommand{\commit}{\dots~(in \emph{packed-refs})}}
As a fallback, this creates the (not-so-useful) output "... (in packed-refs)" instead of a commit ID – but this only lasts until the next commit, when the file heads/branchname
is recreated for the affected branch.
(A more ambitious workaround could parse packed-refs
, of course.)
Full MWE:
\documentclass{article}
\usepackage{xstring}
\usepackage{catchfile}
\CatchFileDef{\headfull}{.git/HEAD.}{}
\StrGobbleRight{\headfull}{1}[\head]
\StrBehind[2]{\head}{/}[\branch]
\IfFileExists{.git/refs/heads/\branch.}{%
\CatchFileDef{\commit}{.git/refs/heads/\branch.}{}}{%
\newcommand{\commit}{\dots~(in \emph{packed-refs})}}
\begin{document}
This revision: \texttt{\commit} on branch \texttt{\branch}.
\end{document}
Sample output:
This revision:
d92dc1386e48e04ceecb85461ed3b232146e6a32
on branchmaster
.
1 Using the last optional argument of \StrGobbleRight
(name
) to assign the trimmed string to a macro (\head
) is necessary to allow further manipulation of the string using the xstring
functions – see here for a discussion.
A no-package approach (tested on mac os, modify it for Windows regarding extra needed dot perhaps) based on @CL.'s answer.
\documentclass{article}
\newcommand\dotGitHEAD{}
\newcommand\branch{}
\newcommand\commit{}
\makeatletter\let\myfilehandle\@inputcheck\makeatother
\openin\myfilehandle=.git/HEAD\relax
\begingroup\endlinechar-1
\global\read\myfilehandle to \dotGitHEAD
\endgroup
\closein\myfilehandle
\newcommand\GetBranch{}
\def\GetBranch ref: refs/heads/#1\relax{\renewcommand{\branch}{#1}}
\expandafter\GetBranch\dotGitHEAD\relax
\openin\myfilehandle=.git/refs/heads/\branch\relax
\begingroup\endlinechar-1
\global\read\myfilehandle to \commit
\endgroup
\closein\myfilehandle
\begin{document}
\branch+++
\commit+++
This revision: \texttt{\commit} on branch \texttt{\branch}.
\end{document}
Package getcommit
:
\ProvidesPackage{getcommit}[2018/10/16 get current commit and branch (JFB)]
\@ifundefined{branch}{}
{\PackageWarning{getcommit}{ATTENTION!^^J
\@spaces\@spaces\string\branch\space macro was already
defined. Overwritten.}}
\@ifundefined{commit}{}
{\PackageWarning{getcommit}{ATTENTION!^^J
\@spaces\@spaces\string\commit\space macro was already
defined. Overwritten.}}
\openin\@inputcheck=.git/HEAD\relax
\begingroup\endlinechar-1
\global\read\@inputcheck to \getcommit@HEAD
\endgroup
\closein\@inputcheck
\def\getcommit@GetBranch ref: refs/heads/#1\relax{\def\branch{#1}}
\expandafter\getcommit@GetBranch\getcommit@HEAD\relax
\openin\@inputcheck=.git/refs/heads/\branch\relax
\begingroup\endlinechar-1
\global\read\@inputcheck to \commit
\endgroup
\closein\@inputcheck
\endinput
Example of use:
\documentclass{article}
\usepackage{getcommit}
\begin{document}
+++\branch+++
+++\commit+++
%\frenchspacing
This revision: \texttt{\commit} on branch \texttt{\branch}.
\end{document}
We can use \input
instead \read
primitive:
\def\gitdir{.git/}
\def\readgitcommitA #1 {\def\gitcommit{#1}}
\def\readgitbranchA #1 #2 {\def\gitbranch{#2}}
\def\readgitcommit{\expandafter\readgitbranchA \input \gitdir HEAD
\expandafter\readgitcommitA \input \gitdir\gitbranch\relax}
\readgitcommit
Now, the commit ID is in "\gitcommit" macro.