% \iffalse vim: expandtab:
% \fi
% \iffalse meta-comment
%<*internal>
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTex\else
  \expandafter\begingroup
\fi
%</internal>
%<*install>
\input docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble
latexgit
Author: Camil Staps <info@camilstaps.nl>
\endpreamble
\postamble
Copyright (c) 2016-2020 Camil Staps <info@camilstaps.nl>
Licensed under GPL v3.
\endpostamble
\usedir{tex/latex/latexgit}
\generate{
  \file{\jobname.sty}{\from{\jobname.dtx}{package}}
}
%</install>
%<install>\endbatchfile
%<*internal>
\usedir{source/latex/latexgit}
\generate{
    \file{\jobname.ins}{\from{\jobname.dtx}{install}}
}
\nopreamble\nopostamble
\ifx\fmtname\nameofplainTeX
  \expandafter\endbatchfile
\else
  \expandafter\endgroup
\fi
%</internal>
%<*driver>
\documentclass{ltxdoc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{\jobname}
\usepackage[numbered]{hypdoc}
\usepackage{showexpl}
\usepackage{xcolor}
\lstset{%
    basicstyle=\ttfamily\small,
    commentstyle=\itshape\ttfamily\small,
    showspaces=false,
    keepspaces=true,
    showstringspaces=false,
    breaklines=true,
    backgroundcolor=\color{lightgray},
    breakautoindent=true,
}
\usepackage{enumitem}
\usepackage{verbatim}
\usepackage{amssymb}
\usepackage{xstring}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\setcounter{IndexColumns}2
\newlist{options}{description}{1}
\setlist[options]{font=\small\texttt,itemsep=0pt,style=nextline}
\newcommand{\git}[0]{\gitcmd{git}}
\newcommand{\gitcmd}[1]{\texttt{\small#1}}
\newcommand{\gitopt}[1]{%
  \texttt{\small\hyperref[options]{\textcolor{black}{#1}}}%
}
\newcommand{\bashcmd}[1]{\texttt{\small#1}}
\newcommand{\texcmd}[1]{\texttt{\small#1}}
\def\GitOptions#1,#2\relax{%
  \gitopt{#1}%
  \begingroup%
  \ifx\relax#2\relax %
    \def\next{\endgroup}%
  \else%
    \def\next{, \endgroup\GitOptions#2\relax}%
  \fi%
  \next%
}
\def\gitoptions#1{%
  Recognised options:
  \begingroup%
  \edef\@tempa{#1,}%
  \expandafter\endgroup%
  \expandafter\GitOptions\@tempa\relax%
  \unskip.
}
\begin{document}
  \DocInput{\jobname.dtx}
  \newpage
  \PrintChanges
  \newpage
  \PrintIndex
\end{document}
%</driver>
% \fi
%
%\GetFileInfo{\jobname.sty}
%
%\title{The \latexgit{} package}
%\author{Camil Staps\thanks{Email: info@camilstaps.nl}}
%\date{^^A
%  Version \gitcommithash[shortHash]\\
%  \small{\gitcommitdate[formatDate,formatTime,formatInterDateTime={,~},showTimeZone]}}
%
%\maketitle
%
%\gitchanges
%
%\DoNotIndex{\def,\edef,\expandafter,\global,\let,\newcommand,\relax,\catcode}
%\DoNotIndex{\%,\&} % this does not work currently due to a known bug
%\DoNotIndex{\begingroup,\endgroup}
%\expandafter\DoNotIndex\expandafter{\string\^}
%\DoNotIndex{\hbox,\ifdim,\setbox,\wd}
%\DoNotIndex{\ifeof,\else,\fi,\newif}
%\DoNotIndex{\NeedsTeXFormat,\PackageError,\ProvidesPackage,\RequirePackage}
%\DoNotIndex{\endlinechar,\newread,\openin,\read,\readline}
%\DoNotIndex{\space,\unskip}
%\DoNotIndex{\LaTeX,\pgfkeys,\changes}
%\DoNotIndex{\formatdate,\formattime}
%\DoNotIndex{\StrSubstitute}
%\DoNotIndex{\git@@revision,\git@@thisdate,\git@@thishash,\git@@thismsg,\git@@parent}
%
%\begin{abstract}
%  This is the documentation of the \latexgit{} package.
%  Several macros are defined to fetch \git{} information and typeset it.
%  The macros defined by \latexgit{} can be helpful to documentation authors
%  and others to whom clear document versioning is important.
%\end{abstract}
%
%\tableofcontents
%
%\section{Overview}
%\label{sec:overview}
%\subsection{Related packages}
%\label{sec:overview:related-packages}
%Brent Longborough's \textsf{gitinfo} and \textsf{gitinfo2} packages have
%similar features, with a different interface. Instead of running \git{}
%commands, it relies on special \git{} files. This has the advantage that it
%works on Windows, however, \git{} hooks have to be put into place.
%
%Andr\'e Hilbig's \textsf{gitfile-info} does something similar as
%\textsf{gitinfo} and \latexgit, but the package manual is in German so who
%knows. It seems that Python and \git{} hooks are required.
%
%\subsection{Dependencies}
%\label{sec:overview:dependencies}
%\latexgit{} gets its information by running \git{} commands. It uses
%\cs{write18} for this, so documents using \latexgit{} will have to be compiled
%with the \bashcmd{-shell-escape} flag.
%
%Most of the information is retrieved using \git{} but then parsed using
%\bashcmd{grep}, \bashcmd{cut} and similar tools. Unfortunately, this is
%necessary: \gitcmd{git log} accepts arbitrary formats, but uses the percent
%sign in these formats, and running commands with percent signs does not seem
%possible using \cs{write18}.
%
%An unfortunate side effect of this is that this package will not work on
%Windows.
%
%\subsection{Getting information}
%\label{sec:overview:getting-information}
%\latexgit{} runs a shell command using \cs{git@command}. This macro reads the
%result into \cs{git@rawresult.} This result contains a newline character at
%the end that needs to be removed to avoid unwanted spacing. Therefore,
%\cs{git@result} is defined as a wrapper for \cs{git@rawresult} that removes
%this spacing with \cs{unskip}.
%
%\subsection{Interface}
%For each information-fetching command, two versions are defined: one, which
%only executes the command (leaving the result available in \cs{git@result});
%and one, which executes the command and writes the result. An example is
%\cs{git@commithash} with its counterpart \cs{gitcommithash}. Usually, the
%latter is most useful.
%
%\section{Options}
%\label{sec:options}
%A number of options is available to all macros that fetch information through
%a \textsf{pgfkeys} interface. All keys are recognised by all macros, but not
%all are considered by each macro. Check the documentation for specific macros
%to see which options are relevant.
%
%\begin{options}
%  \label{options}
%  \item[directory=DIR]
%    Use the git repository in directory \verb$DIR$ (or its first parent
%    directory that is a git repository). \latexgit{} will \bashcmd{cd} to this
%    directory before executing the actual \git{} command.
%  \item[file=FILE]
%    Get the information for the last commit modifying \verb$FILE$.
%  \item[formatDate]
%    Format dates using \textsf{datetime}'s \cs{formatdate}.
%  \item[formatTime]
%    Format times using \textsf{datetime}'s \cs{formattime}.
%  \item[formatInterDateTime]
%    If both \gitopt{formatDate} and \gitopt{formatTime} are set, this is put
%    in between (e.g.  `\verb$\space{}at\space{}$' in English --- this is also
%    the default).
%  \item[revision=REV]
%    Get the hash of revision \verb$REV$ (e.g.  \gitcmd{master},
%    \gitcmd{HEAD\textasciicircum}, etc.). Note that if multiple circumflexes
%    are desired, the catcode of \verb$^$ has to be changed. For example:
%    \LTXinputExample[numbers=none,pos=r]{exmp/catcode-circumflex.tex}
%  \item[showTimeZone]
%    When \gitopt{formatTime} is set: show the time zone between parentheses
%    after the time.
%  \item[shortHash]
%    Get only the first seven characters of the hash, as in \gitcmd{git log
%    -{}-oneline}.
%\end{options}
%
%\section{Examples}
%\label{sec:examples}
%\DescribeMacro{\gitcommithash}
%\DescribeMacro{\gitcommitmsg}
%\DescribeMacro{\gitcommitauthor}
%\DescribeMacro{\gitcommitauthorname}
%\DescribeMacro{\gitcommitauthoremail}
%    These macros are used to get and print basic commit information.
%    \LTXinputExample[pos=b,numbers=none]{exmp/gitcommit.tex}
%
%\DescribeMacro{\gitcommitdate}
%    This macro displays the \git{} commit date.
%    The following example shows the effect of the options
%    \gitopt{formatDate}, \gitopt{formatTime} and \gitopt{showTimeZone}.
%    \LTXinputExample[pos=b,numbers=none]{exmp/gitcommitdate.tex}
%
%\DescribeMacro{\gitcommand}
%    This macro executes an arbitrary \git{} command and directly typesets the
%    result. It only accepts the option \gitopt{directory}.
%    \LTXinputExample[pos=b,numbers=none]{exmp/gitcommand.tex}
%
%\section{Implementation}
%\label{sec:implementation}
%Define the package and load required packages.
%
%    \begin{comment}
%<*package>
%    \end{comment}
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{latexgit}[2020/04/20]

\RequirePackage{pgfkeys}
\RequirePackage{datetime}
%    \end{macrocode}
%
%\begin{macro}{\latexgit}
%    Prints the name of the package.
%    \begin{macrocode}
\newcommand{\latexgit}[0]{\LaTeX{}git}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@result}
%    When a \git{} command is run, the result is stored in \cs{git@rawresult}.
%    However, using \cs{read} results in spacing at the end of the macro.
%    Hence, we need to use \cs{unskip} to remove this spacing.
%    This is a wrapper for \cs{git@rawresult} that adds this \cs{unskip}.
%    \begin{macrocode}
\newcommand{\git@result}[0]{\git@rawresult\unskip}
%    \end{macrocode}
%\end{macro}
%
%    We now define the keys accepted by git macros. The following options are
%    recognised. Check the documentation of the macro to see which options are
%    considered.
%
%    \begin{macrocode}
\newif\ifgit@opt@FormatDate
\newif\ifgit@opt@FormatTime
\newif\ifgit@opt@ShortHash
\newif\ifgit@opt@ShowTimeZone
\pgfkeys{
  /git/.is family, /git,
  default/.style={
    directory=.,
    file=,
    formatDate=false,
    formatInterDateTime=\space{}at\space{},
    formatTime=false,
    revision=HEAD,
    showTimeZone=false,
    shortHash=true,
  },
  directory/.estore in=\git@opt@Directory,
  file/.estore in=\git@opt@File,
  formatDate/.is if=git@opt@FormatDate,
  formatInterDateTime/.estore in=\git@opt@FormatInterDateTime,
  formatTime/.is if=git@opt@FormatTime,
  revision/.estore in=\git@opt@Revision,
  showTimeZone/.is if=git@opt@ShowTimeZone,
  shortHash/.is if=git@opt@ShortHash,
}
%    \end{macrocode}
%
%\begin{macro}{\git@command}
%    Run a \git{} command and read the output into \cs{git@rawresult}. Before
%    running the command, the directory will change to \cs{git@opt@Directory}.
%    \begin{macrocode}
\newread\git@stream%
\newcommand{\git@command}[1]{%
  \def\git@rawresult{}%
  \openin\git@stream|"cd \git@opt@Directory; #1"
  \ifeof\git@stream%
    \PackageError{latexgit}%
      {invoke LaTeX with the -shell-escape flag}%
      {invoke LaTeX with the -shell-escape flag}%
  \else%
    \begingroup%
    \catcode`\^^M9%
    \endlinechar=-1%
    \readline\git@stream to \git@streamoutput%
    \global\let\git@rawresult\git@streamoutput%
    \endgroup%
  \fi%
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommand}
%    A wrapper around \cs{git@command} and \cs{git@result}, to directly typeset the
%    result of arbitrary commands.
%    \begin{macrocode}
\newcommand{\gitcommand}[2][]{%
  \pgfkeys{/git,default,#1}%
  \git@command{git #2}%
  \git@result}
%    \end{macrocode}
%\end{macro}
%
%In what follows, \verb$%$ may be used in calls to \git{}. Therefore we use
%\verb$&$ as comment character.
%    \begin{macrocode}
\catcode`\&=14\catcode`\%=11
%    \end{macrocode}
%
%\begin{macro}{\git@space}
%    For internal use, when a space is required after a \texttt{csname} in a
%    call to \cs{git@command}.
%    \begin{macrocode}
\def\git@space{ }
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommithash}
%    Get a commit hash.
%    \gitoptions{directory,revision,shortHash}
%
%    \begin{macrocode}
\newcommand{\gitcommithash}[1][]{&
  \git@commithash[#1]\git@result}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commithash}
%    Like \cs{gitcommithash}, but does not return the output.
%    \begin{macrocode}
\newcommand{\git@commithash}[1][]{&
  \pgfkeys{/git,default,#1}&
  \ifgit@opt@ShortHash&
    \git@command{git log -n 1 --format=%h
      \git@opt@Revision\git@space -- \git@opt@File}&
  \else&
    \git@command{git log -n 1 --format=%H
      \git@opt@Revision\git@space -- \git@opt@File}&
  \fi&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommitmsg}
%    Get a commit message.
%    \gitoptions{directory,revision}
%
%    \begin{macrocode}
\newcommand{\gitcommitmsg}[1][]{&
  \git@commitmsg[#1]\git@result}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commitmsg}
%    Like \cs{gitcommitmsg}, but does not return the output.
%    \begin{macrocode}
\newcommand{\git@commitmsg}[1][]{&
  \pgfkeys{/git,default,#1}&
  \git@command{git log -n 1 --format=%B
    \git@opt@Revision\git@space -- \git@opt@File}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@formatCommitDate}
%    Format a commit date in ISO format using \textsf{datetime}'s
%    \cs{formatdate}.
%    \begin{macrocode}
\def\git@formatCommitDate#1-#2-#3 #4:#5:#6 +#7\relax{&
  \formatdate{#3}{#2}{#1}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@formatCommitTime}
%    Format a commit time using \textsf{datetime}'s \cs{formattime}.
%    \gitoptions{showTimeZone}
%    \begin{macrocode}
\def\git@formatCommitTime#1-#2-#3 #4:#5:#6 +#7\relax{&
  \formattime{#4}{#5}{#6}&
  \ifgit@opt@ShowTimeZone&
    \space(+#7\unskip)&
  \fi&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommitdate}
%    Get a commit date. If \gitopt{formatDate} is set,
%    \cs{git@formatCommitDate} will be used. If \gitopt{formatTime} is set, and
%    \gitopt{formatDate} is unset, \cs{git@formatCommitTime} will be used.
%    \gitoptions{directory,formatDate,formatTime,revision,showTimeZone}
%
%    \begin{macrocode}
\newcommand{\gitcommitdate}[1][]{&
  \git@commitdate[#1]&
  \ifgit@opt@FormatDate&
    \expandafter\git@formatCommitDate\git@rawresult\relax&
    \ifgit@opt@FormatTime&
      \git@opt@FormatInterDateTime&
      \expandafter\git@formatCommitTime\git@rawresult\relax&
    \fi
  \else\ifgit@opt@FormatTime&
    \expandafter\git@formatCommitTime\git@rawresult\relax&
  \else
    \git@result&
  \fi\fi&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commitdate}
%    Like \cs{gitcommitdate}, but does not return the output.
%    \begin{macrocode}
\newcommand{\git@commitdate}[1][]{&
  \pgfkeys{/git,default,#1}&
  \git@command{git log -n 1 --format=%ai
    \git@opt@Revision\git@space -- \git@opt@File}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommitauthor}
%    Get a commit author.
%    \gitoptions{directory,revision}
%
%    \begin{macrocode}
\newcommand{\gitcommitauthor}[1][]{&
  \git@commitauthor[#1]\git@result}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commitauthor}
%    Like \cs{gitcommitauthor}, but does not return the output.
%    \begin{macrocode}
\newcommand{\git@commitauthor}[1][]{&
  \pgfkeys{/git,default,#1}&
  \git@command{git log -n 1 --format='%an <%ae>'
    \git@opt@Revision\git@space -- \git@opt@File}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommitauthorname}
%    Get a commit author's name.
%    \gitoptions{directory,revision}
%
%    \begin{macrocode}
\newcommand{\gitcommitauthorname}[1][]{&
  \git@commitauthorname[#1]\git@result}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commitauthorname}
%    Like \cs{gitcommitauthorname}, but does not return the output.
%    \begin{macrocode}
\newcommand{\git@commitauthorname}[1][]{&
  \pgfkeys{/git,default,#1}&
  \git@command{git log -n 1 --format=%an
    \git@opt@Revision\git@space -- \git@opt@File}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitcommitauthoremail}
%    Get a commit author's email address.
%    \gitoptions{directory,revision}
%
%    \begin{macrocode}
\newcommand{\gitcommitauthoremail}[1][]{&
  \git@commitauthoremail[#1]\git@result}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commitauthoremail}
%    Like \cs{gitcommitauthoremail}, but does not return the output.
%    \begin{macrocode}
\newcommand{\git@commitauthoremail}[1][]{&
  \pgfkeys{/git,default,#1}&
  \git@command{git log -n 1 --format=%ae
    \git@opt@Revision\git@space -- \git@opt@File}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@commitparent}
%    Get the hash of the first parent.
%    \begin{macrocode}
\newcommand{\git@commitparent}[1][]{&
  \pgfkeys{/git,default,#1}&
  \git@command{git log -n 1 --format=%p
    \git@opt@Revision\git@space -- \git@opt@File
    | cut -d' ' -f2}&
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\gitchanges}
%    Record the full \git{} commit history (following first parents using
%    \cs{git@commitparent}) using \cs{changes}.
%    \begin{macrocode}
\newcommand{\gitchanges}[1][]{&
  \git@changes[#1]{HEAD}
}
%    \end{macrocode}
%\end{macro}
%
%\begin{macro}{\git@changes}
%    Like \cs{gitchanges}, but accepts an extra argument for the revision.
%    \begin{macrocode}
\newcommand{\git@changes}[2][]{&
  \edef\git@@revision{#2}&
  \git@commithash[revision=\git@@revision]&
  \edef\git@@thishash{\git@rawresult}&
  \git@command{git log -n 1 --format=%ad --date=short \git@opt@Revision}&
  \edef\git@@thisdate{\git@rawresult}&
  \git@commitmsg[revision=\git@@revision]&
  & TODO: this removes '=' characters because they break \changes, but the real
  & solution would be to put something back that restores these characters.
  \StrSubstitute[0]{\git@rawresult}{=}{}[\git@@thismsg]&
  \changes{\git@@thisdate\unskip: \git@@thishash}{\git@@thisdate}{\git@@thismsg}&
  \git@commitparent[revision=\git@@revision]&
  \let\git@@parent\git@rawresult&
  \setbox0=\hbox{\git@@parent\unskip}\ifdim\wd0=0pt
  \else&
    \git@changes{\git@@parent}&
  \fi&
}
%    \end{macrocode}
%\end{macro}
%
%Reset the catcodes for \verb$%$ and \verb$&$:
%    \begin{macrocode}
\catcode`\&=4\catcode`\%=14
%    \end{macrocode}
%
%    \begin{comment}
%</package>
%    \end{comment}
%
%\Finale
\endinput
