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 branch master.


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}

enter image description here



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.