Linux ip-172-26-7-228 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64
Your IP : 18.216.253.84
#!/usr/bin/env perl
# @author Robin Schneider <ypid23@aol.de>
# @licence GPLv3+ <http://www.gnu.org/licenses/gpl.html>
# Modules and pragmas {{{
use strict;
use warnings;
use autodie;
use feature qw(say);
use utf8;
use open qw(:std :utf8);
binmode STDOUT, ':encoding(UTF-8)';
use IPC::System::Simple qw(capturex);
use Getopt::Long;
use Pod::Usage;
# Get the path where this script is located {{{
my $dirpath;
BEGIN {
use File::Basename qw(fileparse);
use Cwd qw(abs_path);
$dirpath = ( fileparse( abs_path($0) ) )[1];
}
# }}}
use Locale::Maketext::Simple (
Path => $dirpath . 'po/',
Decode => 1,
);
# }}}
# Optional parameters {{{
my $print_author = 0;
my $commit_count = 1;
my $width = 0;
my $lang = undef;
my $version = 0;
my $help = 0;
my $man = 0;
my ( $git_user, $git_repo, $git_commit_address );
GetOptions(
'author' => \$print_author,
'startcommit=s' => \$commit_count,
'width=s' => \$width,
'git-c-add=s' => \$git_commit_address,
'user=s' => \$git_user,
'repo=s' => \$git_repo,
'lang=s' => \$lang,
'version' => \$version,
'help|?' => \$help,
'man' => \$man,
);
pod2usage(1) if $help;
pod2usage( -verbose => 2, -exitstatus => 0, ) if $man;
pod2usage( -verbose => 99, -sections => 'Version' ) if $version;
my $git_remote = ( split /\n/xms, capturex(qw(git remote -v)) )[0];
loc_lang($lang) if ( defined $lang );
if ( not defined $git_user
and not defined $git_repo
and defined $git_remote
and $git_remote =~ m#\s.*?:(?<user>\w+?)/(?<repo>.*?)\.git\ \(fetch\)\Z#xms )
{
$git_user = $+{user};
$git_repo = $+{repo};
}
say '%% This file was generated by the script latex-git-log';
my $git_command_commit_msg = '%s';
if ( defined $git_user and defined $git_repo ) {
unless ( defined $git_commit_address ) {
$git_commit_address = "https://github.com/$+{user}/$+{repo}/commit";
}
$git_command_commit_msg = '%H & %s';
say '%% Base git commit URL: ' . $git_commit_address;
}
if ($width) {
$width = "p{${width}cm}";
}
else {
$width = 'l';
}
# }}}
# LaTeX template {{{
say '\begin{tabular}{lp{12cm}}
\label{tabular:legend:git-log}
\textbf{' . loc('acronym') . '} & \textbf{' . loc('meaning') . '} \\\\
V & \texttt{' . loc('version') . '} \\\\
' . loc('tag') . ' & ' . loc('\texttt{git tag}') . ' \\\\
' . loc('MF') . ' & ' . loc('Number of \texttt{modified files}.') . ' \\\\
' . loc('AL') . ' & ' . loc('Number of \texttt{added lines}.') . ' \\\\
' . loc('DL') . ' & ' . loc('Number of \texttt{deleted lines}.') . ' \\\\
\end{tabular}
\bigskip
\iflanguage{ngerman}{\shorthandoff{"}}{}';
if ($print_author) {
say "\\begin{longtable}{|rllllrrr|}";
}
else {
say "\\begin{longtable}{|rll${width}rrr|}";
}
say '\hline \multicolumn{1}{|c}{\textbf{' . loc('V') . '}} & \multicolumn{1}{c}{\textbf{' . loc('tag') . '}}';
say '& \multicolumn{1}{c}{\textbf{' . loc('author') . '}}' if $print_author;
say '& \multicolumn{1}{c}{\textbf{' . loc('date') . '}}
& \multicolumn{1}{c}{\textbf{' . loc('commit message') . '}} & \multicolumn{1}{c}{\textbf{' . loc('MF') . '}}
& \multicolumn{1}{c}{\textbf{' . loc('AL') . '}} & \multicolumn{1}{c|}{\textbf{' . loc('DL') . '}} \\\\ \hline
\endhead
';
if ($print_author) {
say '\hline \multicolumn{8}{|r|}{\longtableendfoot} \\\\ \hline';
}
else {
say '\hline \multicolumn{7}{|r|}{\longtableendfoot} \\\\ \hline';
}
say '\endfoot
\hline% \hline
\endlastfoot
';
# }}}
# Get version history from git log {{{
# git log --pretty=format:'%ai'
# git log --date=short --pretty=format:'%ad'
my @lines;
my @git_command = qw(git log --date=short --shortstat);
if ($print_author) {
push( @git_command, qq(--pretty=format:%H & %an NoTinAuthorFiled& %ad & $git_command_commit_msg) );
}
else {
push( @git_command, qq(--pretty=format:%H %ad & $git_command_commit_msg) );
}
@lines = reverse capturex(@git_command);
# }}}
# Get tags {{{
my @tags_commits = capturex( 'git', 'for-each-ref', '--format=%(refname:short) %(objectname)', 'refs/tags' );
my %commit_tags; # The key will be a SHA1 commit hash and the value a comma separated list of all tags.
for (@tags_commits) {
my ( $tag, $c_hash ) = split /\s/xms, $_;
chomp($c_hash);
if ( defined $commit_tags{$c_hash} ) {
$commit_tags{$c_hash} .= ", $tag";
}
else {
$commit_tags{$c_hash} = $tag;
}
}
# }}}
# Helper function {{{
sub latex_escape {
# Source: http://ommammatips.blogspot.de/2011/01/perl-function-for-latex-escape.html
my $paragraph = shift;
# Replace a \ with $\backslash$
# This is made more complicated because the dollars will be escaped
# by the subsequent replacement. Easiest to add \backslash
# now and then add the dollars
$paragraph =~ s/\\/\\backslash/g;
# Must be done after escape of \ since this command adds latex escapes
# Replace characters that can be escaped
$paragraph =~ s/([\$\#&%_{}])/\\$1/g;
# Replace ^ characters with \^{} so that $^F works okay
$paragraph =~ s/(\^)/\\$1\{\}/g;
# Replace tilde (~) with \texttt{\~{}}
# $paragraph =~ s/~/\\texttt\{\\~\{\}\}/g;
$paragraph =~ s/~/\\~\{\}/g;
# Now add the dollars around each \backslash
$paragraph =~ s/(\\backslash)/\$$1\$/g;
return $paragraph;
} ## end sub latex_escape
# }}}
# Loop over all commits {{{
my $which_line = 0;
my @changes;
for (@lines) {
next if /\A\Z/xms;
chomp;
if ($which_line) {
s/\A([0-9a-f]{40})\s//xms or die "Did not match the commit hash\n";
my $tags = exists $commit_tags{$1} ? $commit_tags{$1} : q();
my $date_author = '';
my $c_msg;
if ($print_author) {
/(?:& )(.*?)NoTinAuthorFiled(& .*? &) (.*)/;
$date_author = latex_escape($1) . $2;
$c_msg = $3;
}
else {
/(.*? &) (.*)/;
$date_author = $1;
$c_msg = $2;
}
if ( defined $git_user and defined $git_repo ) {
$c_msg =~ /(.*?) & (.*)/;
$c_msg = sprintf '\\href{%s/%s}{%s}', $git_commit_address, $1, latex_escape($2);
}
else {
$c_msg = latex_escape($c_msg);
}
say "\\hline $commit_count & $tags & $date_author $c_msg & " . join( ' & ', @changes ) . ' \\\\';
$commit_count++;
} ## end if ($which_line)
else {
@changes = ( 0, 0, 0 );
/(\d+) files? changed/ and $changes[0] = $1;
/(\d+) insertions?/ and $changes[1] = $1;
/(\d+) deletions?/ and $changes[2] = $1;
}
$which_line ^= 1; ## toggle bit
} ## end for (@lines)
say '\end{longtable}';
# }}}
__END__
# Documentation {{{
=head1 NAME
latex-git-log - Generates the version history of a git project as LaTeX source code.
=head1 SYNOPSIS
latex-git-log [options]
Options:
--author set this if you want the author included
--startcommit set the start value of count commit
--width set the width in cm of the commit message field in the LaTeX table
--git-c-add set an base URL to link to a commit
--user set a github user to derive the base URL
--repo set a github repository to derive the base URL
--lang language of the legend and all strings in the output
--version, -v print version of this script
--help brief help message
--man full documentation
=head1 OPTIONS
=over 8
=item B<--width>
Set the width in cm of the commit message field in the LaTeX table. If this
parameter is not set then the table is not vertically limited. That means that
if you have a very long commit message then the table will probably not fit on
the page and you will get a "Overfull" error message from TeX.
In this case you should specify the width of the column containing the commit messages.
I normally use something like --width=14 for DIN A4 in landscape.
=item B<--git-c-add>
Set an base URL to link to a commit.
This script will automatically try to use the base URL for github.
=item B<--lang>
Set the language of the legend and all strings in the output. Be default the
language of your system is used but you can overwrite this with this parameter.
Currently this script only supports English and German. If you need a translation
to another language then you can either create a .po file or I can register
this project on one of those websites for online translation.
=back
=head1 Example
You can use it like this:
latex-git-log --width=6 --lang=en > example-output.tex
=head1 DEPENDENCIES
=head2 Of this script
This module requires these other modules and libraries:
IPC::System::Simple
Locale::Maketext::Simple
Everything else should already be installed.
=head2 To compile the output
The table is using the B<longtable> package and the links to a web resource for
each commit use the \href macro from B<hyperref>. So these two packages have to
be loaded.
Furthermore you need to defined the macro B<\longtableendfoot> which will be
expanded on the bottom of every page if the table will be continued on the next
page. You can defined it to a localized message to inform the reader that this
table is not complete and will be continued.
=head1 DESCRIPTION
B<This program> will output the entire version history as table written in
LaTeX if it is executed within a git repository.
It is intended that you redirect the standard output of this script to a file
which can then be included from your main TeX document.
Because the table can be very large you might want to put the thing on a
landscape page.
=head1 Version
0.9
=head1 AUTHOR
Robin Schneider <ypid23@aol.de>
=head1 Development
Source code repository: https://github.com/ypid/typesetting/tree/master/scripts/latex-git-log
Please report bugs and feature requests at https://github.com/ypid/typesetting/issues
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2012-2013 by Robin Schneider
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Dieses Programm ist Freie Software: Sie können es unter den Bedingungen
der GNU General Public License, wie von der Free Software Foundation,
Version 3 der Lizenz oder (nach Ihrer Option) jeder späteren
veröffentlichten Version, weiterverbreiten und/oder modifizieren.
Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber
OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
Siehe die GNU General Public License für weitere Details.
Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.
=cut
# }}}
|