2 Star 0 Fork 0

mirrors_openSUSE/linuxrc

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
git2log 22.66 KB
一键复制 编辑 原始数据 按行查看 历史
Ladislav Slezák 提交于 2017-10-20 19:16 +08:00 . Update git2log script
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
#! /usr/bin/perl
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# This script is maintained at https://github.com/openSUSE/linuxrc-devtools
#
# If you're in another project, this is just a copy.
# You may update it to the latest version from time to time...
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
use strict;
use Getopt::Long;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 1;
sub usage;
sub changelog_outdated;
sub get_github_project;
sub get_version;
sub get_tags;
sub get_log;
sub is_formatted_tag;
sub get_branch;
sub choose_tags;
sub add_head_tag;
sub tags_to_str;
sub format_log;
sub format_all_logs;
sub fix_dates;
sub add_line_breaks;
sub format_date_obs;
sub format_date_iso;
sub raw_date_to_s;
usage 0 if !@ARGV;
my @changelog_deps = qw ( .git/HEAD .git/refs/heads .git/refs/tags );
my $branch;
my $current_version;
my @tags;
my @all_tags;
my $config;
my $opt_log;
my $opt_version;
my $opt_branch;
my $opt_update;
my $opt_file;
my $opt_start;
my $opt_max;
my $opt_width = 66;
my $opt_width_fuzz = 8;
my $opt_sep_width = 68;
my $opt_format = 'internal'; # obs, internal
my $opt_merge_msg_before = 1; # log auto generated pr merge message before the commit messages (vs. after)
my $opt_join_author = 1; # join consecutive commit messages as long as they are by the same author
my $opt_keep_date = 1; # don't join consecutive commit messages if they have different time stamps
GetOptions(
'help' => sub { usage 0 },
'version' => \$opt_version,
'branch' => \$opt_branch,
'update' => \$opt_update,
'start=s' => \$opt_start,
'format=s' => \$opt_format,
'max=i' => \$opt_max,
'width=i' => \$opt_width,
'fuzz=i' => \$opt_width_fuzz,
'merge-msg=s' => sub { $opt_merge_msg_before = ($_[1] eq 'after' ? 0 : 1) },
'join-author!' => \$opt_join_author,
'keep-date!' => \$opt_keep_date,
'log|changelog' => \$opt_log,
) || usage 1;
# ensure we are used correctly
usage 1 if @ARGV > 1 || !($opt_log || $opt_version || $opt_branch);
$opt_file = @ARGV ? shift : '-';
die "no git repo\n" unless -d ".git";
# if update option has been give write changelog only if git refs are newer
exit 0 if $opt_update && $opt_file ne '-' && -f($opt_file) && !changelog_outdated($opt_file);
$opt_max = 2 if $opt_version || $opt_branch;
# gather some data
get_github_project;
get_branch;
get_log;
fix_dates;
get_tags;
choose_tags;
add_head_tag;
get_version;
# just print current branch
if($opt_branch) {
open my $f, ">$opt_file";
print $f $config->{branch} ? $config->{branch} : "master", "\n";
close $f;
exit 0;
}
# just print current version
if($opt_version) {
my $old_version;
if($opt_file ne '-' && open(my $f, $opt_file)) {
chomp($old_version = <$f>);
close $f;
}
if($config->{version} ne $old_version) {
open my $f, ">$opt_file";
print $f "$config->{version}\n";
close $f;
}
exit 0;
}
# set start tag
if($opt_start) {
my $x = is_formatted_tag $opt_start;
die "$opt_start: not a valid start tag\n" if !$x;
$x->{branch} = $config->{branch} if !$x->{branch};
$config->{start} = $x;
}
format_all_logs;
open my $f, ">$opt_file";
print $f $_->{formatted} for @{$config->{log}};
close $f;
exit 0;
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# usage(exit_code)
#
# Print help message and exit.
# - exit_code: exit code
#
# Function does not return.
#
sub usage
{
my $err = shift;
print <<" usage";
Usage: git2log [OPTIONS] [FILE]
Create changelog and project version from git repo.
--changelog Write changelog to FILE.
--version Write version number to FILE.
--branch Write current branch to FILE.
--start START_TAG Start with tag START_TAG.
--max N Write at most MAX long entries.
--update Write changelog or version only if FILE is outdated.
--format FORMAT Write log using FORMAT. Supported FORMATs are 'internal' (default) and 'obs'.
--width WIDTH Reformat log entries to be max WIDTH chars wide.
--fuzz FUZZ Allow log lines to be up to FUZZ chars longer as WIDTH to avoid
line breaks leaving tiny bits on the last line.
--merge-msg WHERE Log message about merges before or after the actual merge commit messages.
Valid values for WHERE are 'after' and 'before' (default).
--join-author Join consecutive commits as long as they are by the same author. (default)
--no-join-author Keep consecutive commits by the same author separate.
--keep-date Join consecutive commits only if they have the same date. (default)
--no-keep-date Join consecutive commits even if dates differ.
--help Print this help text.
usage
exit $err;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# res = changelog_outdated(file)
#
# Return status of changelog file.
# - file: changelog file name
# - res: status
# 1: file is newer than the last git repo change and should be updated
# 0: file is still recent enough
#
# Relies on global var @changelog_deps.
#
sub changelog_outdated
{
my $file = $_[0];
my $changelog_time = (stat $file)[9];
return 1 if !defined $changelog_time;
for (@changelog_deps) {
return 1 if (stat)[9] > $changelog_time;
}
return 0;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_github_project()
#
# Set $config->{github_project} to the github project name.
#
sub get_github_project
{
if(`git config remote.origin.url` =~ m#github.com[:/]+(\S+/\S+)#) {
$config->{github_project} = $1;
$config->{github_project} =~ s/\.git$//;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_version()
#
# Set $config->{branch} and $config->{version} to the current branch and
# version info.
#
# This might be taken directly from HEAD if HEAD is tagged or otherwise be
# exprapolated from the most recent tag (cf. add_head_tag()).
#
sub get_version
{
$config->{version} = "0.0";
my $tag = $config->{log}[0]{tags}[0];
if($tag->{version}) {
$config->{version} = $tag->{version};
$config->{branch} = $tag->{branch};
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_tags()
#
# Parse $config->{raw_log}, extract tag names, and split into per-tag
# sections.
#
# Only tags recognized by is_formatted_tag() are considered.
#
# The parsed logs is stored in $config->{log}, an array of log sections.
# Each section is a hash with these keys:
# - 'tags': array of tags for this section
# - 'commit': git commit id associated with these tags
# - 'lines': git log lines
#
sub get_tags
{
my $log_entry;
for (@{$config->{raw_log}}) {
if(/^commit (\S+)( \((.*)\))?/) {
my $commit = $1;
my $tag_list = $3;
my $xtag;
for my $t (split /, /, $tag_list) {
if($t =~ /tag: (\S+)/) {
my $tag = $1;
my $x = is_formatted_tag $tag;
push @$xtag, $x if $x;
}
}
if($xtag) {
if($log_entry) {
push @{$config->{log}}, $log_entry;
last if $opt_max && @{$config->{log}} >= $opt_max;
}
$log_entry = { commit => $commit, tags => $xtag };
}
else {
$log_entry = { commit => $commit } if !$log_entry;
}
}
push @{$log_entry->{lines}}, $_ if $log_entry;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_log()
#
# Read git log and store lines as array in $config->{raw_log} (trailing
# newlines removed).
#
sub get_log
{
chomp(@{$config->{raw_log}} = `git log --pretty=medium --date=raw --topo-order --decorate`);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# hash_ref = is_formatted_tag(tag_name)
#
# Parse tag and return hash ref with branch and version number parts or
# undef if it doesn't match.
# - tag_name: tag as string
# - hash_ref: hash ref with internal tag representation (with keys 'branch' and 'version').
#
# This expects tags of the form "VERSION" or "BRANCH-VERSION" where VERSION
# consists of decimal numbers separated by dots '.' and BRANCH can be any
# string.
# (Note: it doesn't really have to be the name of an existing branch.)
#
# Tags not conforming to this convention are ignored.
#
sub is_formatted_tag
{
if($_[0] =~ /^((.+)-)?((\d+\.)*\d+)$/) {
return { branch => $2, version => $3 }
}
return undef;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_branch()
#
# Get currently active git branch and store in $config->{branch}.
#
# 'master' branch is represented by empty 'branch' key.
#
sub get_branch
{
chomp(my $branch = `git rev-parse --abbrev-ref HEAD`);
$branch = "" if $branch eq 'master';
$config->{branch} = $branch;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# res = tag_sort(a, b)
#
# Compare 2 tags.
# - a, b: refs to tag hash
# - res: -1, 0, 1
#
# This is used when we have to decide between alternative tags.
# (Prefer 'lesser' variant.)
#
sub tag_sort
{
my ($x, $y);
$x = length $a->{version};
$y = length $b->{version};
# longer version number first
return $y <=> $x if $y <=> $x;
return $a->{branch} cmp $b->{branch};
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# str = tag_to_str(tag_ref)
#
# Convert tag into string.
# - tag_ref: ref to hash with 'branch' and 'version' keys
# - str: string (e.g. "foo-1.44")
#
# 'master' branch is represented by missing/empty 'branch' key.
#
sub tag_to_str
{
my $tag = $_[0];
my $str;
$str = "$tag->{branch}-" if $tag->{branch} ne "";
$str .= $tag->{version};
return $str;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# str = tags_to_str(tag_array_ref)
#
# Convert array of tags into string.
# - tag_array_ref: ref to array of tags
# - str: string (e.g. "(tag1, tag2)"
#
# This function is used only internally for debugging.
#
sub tags_to_str
{
my $tags = $_[0];
my $str;
for my $t (@$tags) {
$str .= ", " if $str;
$str .= tag_to_str $t;
}
return "($str)";
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# choose_tags()
#
# Scan commit messages and extract tag & branch information.
#
# This stores the tag/branch info in $config->{log}[]{tags}.
#
sub choose_tags
{
my $branch = $config->{branch};
for my $x (@{$config->{log}}) {
# printf "# %s\n", tags_to_str($x->{tags});
# no tag info? -> ignore
next if !$x->{tags};
# single tag? -> remember branch info
if(@{$x->{tags}} == 1) {
$branch = $x->{tags}[0]{branch};
next;
}
# several tags? -> choose one
# any with current branch name?
my @t = grep { $_->{branch} eq $branch } @{$x->{tags}};
# no? -> choose among all
@t = @{$x->{tags}} if @t == 0;
# prefer longest version number, then alphanumerically smallest branch name
@t = sort tag_sort @t;
$branch = $t[0]{branch};
$x->{tags} = [ $t[0] ];
# printf "X %s\n", tags_to_str($x->{tags});
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# add_head_tag()
#
# Suggest tag for HEAD if there isn't one.
#
# Basically, use branch + version from most recent tag and increment version.
#
sub add_head_tag
{
return if @{$config->{log}} < 2;
# HEAD tagged already?
return if $config->{log}[0]{tags};
# the first tagged commit if HEAD isn't tagged
my $tag = { %{$config->{log}[1]{tags}[0]} };
# increment version
$tag->{version} =~ s/(\d+)$/$1 + 1/e;
$config->{log}[0]{tags}[0] = $tag;
# remember that the tag was generated
$config->{log}[0]{was_untagged} = 1;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# fix_dates()
#
# Adjust time stamps in entire git log.
#
# The time stamps of the git commits are not necessarily ordered by date.
# But the generated changelog is required to have a monotonic time.
#
# We do this by going through the log in reverse and rewriting any dates we
# find whenever the date decreases.
#
# Not very subtle but it works.
#
sub fix_dates
{
my $last_date;
for (reverse @{$config->{raw_log}}) {
# e.g. "Date: 1443184889 +0200"
if(/^(Date:\s+)(\S+)(\s+\S+)/) {
if(defined $last_date && $2 < $last_date) {
$_ = "$1$last_date$3\n";
next;
}
}
$last_date = $2;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# format_all_logs()
#
# Format the entire git log.
#
# This is done for every code version individually (the log has already been
# split accordingly).
#
# If $config->{start} is set, use this as starting point. Else format the
# entire git log.
#
sub format_all_logs
{
# check if start tag actually exists - if not, print nothing
if($config->{start}) {
my $tag_found;
for (@{$config->{log}}) {
$tag_found = 1, last if grep { tag_to_str($config->{start}) eq tag_to_str($_) } @{$_->{tags}};
}
return if !$tag_found;
}
for (@{$config->{log}}) {
if($config->{start}) {
# stop if we meet the start tag
last if grep { tag_to_str($config->{start}) eq tag_to_str($_) } @{$_->{tags}};
}
format_log $_;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# format_log(log)
#
# Format log messages.
# - log: is an array ref with individual commits
#
# All commits belong to a specific code version (stored in $log->{tag}).
# $log->{formatted} holds the result.
#
# The process is done in several individual steps, documented below in the code.
#
sub format_log
{
my $log = $_[0];
my $merge;
my $commit;
my $saved_commit;
my $commits;
for (@{$log->{lines}}) {
if(/^commit (\S+)/) {
$commit = { ref => $1 };
push @{$commits}, $commit;
if(
$merge &&
$merge->{merge_end} eq substr($commit->{ref}, 0, length($merge->{merge_end}))
) {
undef $merge;
}
if($merge) {
$commit->{merge_ref} = $merge->{ref};
$commit->{date} = $merge->{date};
# add to all commits so it's not lost when we re-arrange
$commit->{merge_msg} = $merge->{msg} if $merge->{msg};
# saved entry no longer needed
undef $saved_commit;
}
next;
}
if(/^Merge: (\S+)/) {
if($commit) {
$merge = { merge_end => $1, ref => $commit->{ref} } unless $merge;
$saved_commit = pop @{$commits};
}
undef $commit;
next;
}
if(/^Date:\s+(\S.*)/) {
if($commit) {
$commit->{date} = $1 if !$commit->{date};
}
elsif($merge) {
$merge->{date} = $1 if !$merge->{date};
}
next;
}
if(/^Author:\s+(\S.*)/) {
$commit->{author} = $1 if $commit;
$merge->{author} = $1 if $merge && !$merge->{author};
next;
}
if($commit) {
push @{$commit->{lines}}, $_ if s/^ //;
}
elsif($merge && !$merge->{msg}) {
if(/^ Merge pull request (#\d+) from (\S+)/) {
if($config->{github_project}) {
$merge->{msg} = "merge gh#$config->{github_project}$1";
}
else {
$merge->{msg} = "merge pr $2";
}
}
elsif(/^ Merge branch '([^']+)'/) {
$merge->{msg} = "merge branch $1";
}
}
}
# it can happen that there's a lonely merge commit left at the end
if($merge && $saved_commit) {
$saved_commit->{merge_ref} = $merge->{ref};
$saved_commit->{date} = $merge->{date};
$saved_commit->{author} = $merge->{author};
$saved_commit->{merge_msg} = $merge->{msg} if $merge->{msg};
$saved_commit->{formatted} = [];
push @{$commits}, $saved_commit;
}
# Note: the individual steps below work on the array @$commits and modify
# its content.
# step 1
# - if there are paragraphs starting with '@log@' or '@+log@'
# - delete first paragraph (short summary)
# - else
# - keep only first paragraph
# - if there is a paragraph starting with '@-log', delete entire log
# - tag commits that have a '@log@' tag so we can delete untagged commits
# belonging to the same merge commit later
my $tagged_merges = {};
for my $commit (@$commits) {
my $para_cnt = 0;
my $delete_all = 0;
my $delete_first = 0;
for (@{$commit->{lines}}) {
$para_cnt++ if $_ eq "";
$para_cnt = 0, $delete_first = 1 if /^\@\+log\@/;
$delete_all = 1 if /^\@\-log\@/;
if(/^\@log\@/) {
$para_cnt = 0;
$commit->{clear} = 1;
$tagged_merges->{$commit->{merge_ref}} = 1 if $commit->{merge_ref} || $log->{was_untagged};
}
$_ = undef if $para_cnt;
}
shift @{$commit->{lines}} if $delete_first;
$commit->{lines} = [] if $delete_all;
}
# step 2
# - clean up tagged commits or commits belonging to tagged merges
for my $commit (@$commits) {
next unless $commit->{clear} || $tagged_merges->{$commit->{merge_ref}};
for (@{$commit->{lines}}) {
last if /^\@\+?log\@/;
$_ = undef;
}
}
# step 3
# - join lines
for my $commit (@$commits) {
my $lines;
my $line;
for (@{$commit->{lines}}) {
next if $_ eq "";
if(
s/^\s*[+\-][\-\s]*// ||
s/^\@\+?log\@// ||
$line eq ""
) {
s/^\s*//;
push @$lines, $line if $line ne "";
$line = $_;
}
else {
s/^\s*//;
$line .= " " if $line ne "";
$line .= $_;
}
}
push @$lines, $line if $line ne "";
$commit->{formatted} = $lines if $lines;
}
# step 4
# - fix small glitches
for my $commit (@$commits) {
next unless $commit->{formatted};
for (@{$commit->{formatted}}) {
s/(fate|bnc|bsc)\s*(#\d+)/\L$1\E$2/ig;
}
}
# step 5
# - add merge info at the top or bottom (depending on $opt_merge_msg_before)
my $merge_logged;
for my $commit ($opt_merge_msg_before ? reverse(@$commits) : @$commits) {
next unless $commit->{formatted};
if($commit->{merge_ref} && !$merge_logged->{$commit->{merge_ref}}) {
$merge_logged->{$commit->{merge_ref}} = 1;
if($commit->{merge_msg}) {
if($opt_merge_msg_before) {
unshift @{$commit->{formatted}}, $commit->{merge_msg};
}
else {
push @{$commit->{formatted}}, $commit->{merge_msg};
}
}
}
}
# step 6
# - join commit messages with same author (optionally even with different dates)
my $commit0;
for my $commit (@$commits) {
next if !$commit->{formatted};
$commit0 = $commit, next if !$commit0;
if(
# $commit->{merge_ref} eq $commit0->{merge_ref} &&
(
$opt_join_author && ($commit->{author} eq $commit0->{author})
&& (!$opt_keep_date || $commit->{date} eq $commit0->{date})
)
|| $opt_format eq 'internal'
) {
unshift @{$commit0->{formatted}}, @{$commit->{formatted}};
delete $commit->{formatted};
}
else {
$commit0 = $commit;
}
}
# step 7
# - add version tag at the end of the first log entry
for my $commit (@$commits) {
next unless $commit->{formatted};
if($opt_format eq 'obs') {
push @{$commit->{formatted}}, $log->{tags}[0]{version} if defined $log->{tags}[0]{version};
}
else {
# push @{$commit->{formatted}}, tag_to_str($log->{tags}[0]);
}
last;
}
# step 8
# - add line breaks
for my $commit (@$commits) {
next unless $commit->{formatted};
for (@{$commit->{formatted}}) {
$_ = add_line_breaks $_;
}
}
# step 9
# - generate final log message
#
# note: non-(open)suse email addresses are replaced by a generic
# 'opensuse-packaging@opensuse.org'
my $formated_log;
for my $commit (@$commits) {
next unless $commit->{formatted} && @{$commit->{formatted}};
if($opt_format eq 'obs') {
$formated_log .= "-" x $opt_sep_width . "\n";
$formated_log .= format_date_obs($commit->{date});
}
else {
$formated_log .= format_date_iso($commit->{date});
}
if($opt_format eq 'obs') {
my $auth = $commit->{author};
$auth =~ s/^.*<//;
$auth =~ s/>.*$//;
# replace non-suse e-mail addresses with a generic one
if($auth !~ /\@(suse\.(com|cz|de)|opensuse\.org)$/) {
$auth = 'opensuse-packaging@opensuse.org'
}
$formated_log .= " - $auth\n\n";
}
else {
$formated_log .= ":\t" . tag_to_str($log->{tags}[0]) . "\n";
}
for (@{$commit->{formatted}}) {
s/^/\t/mg if $opt_format eq 'internal';
$formated_log .= "$_\n";
}
$formated_log .= "\n";
}
$log->{formatted} = $formated_log;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# new_text = add_line_breaks(text)
#
# Add line breaks to text.
# - text: some text
# - new_text: same text, reformatted
#
# Lines are formatted to have a maximal length of $opt_width. If this causes
# the last line to be shorter than $opt_width_fuzz, it is appended to the
# previous line.
#
sub add_line_breaks
{
my @words = split /\s+/, @_[0];
my $remaining_len = length(join '', @words);
my $str = shift(@words);
my $len = length $str;
my $next_len;
my $word_len;
for (@words) {
$word_len = length;
$remaining_len -= $word_len;
$next_len = $len + $word_len + 1;
if(
$next_len >= $opt_width &&
$next_len + $remaining_len + 1 >= $opt_width + $opt_width_fuzz
) {
$str .= "\n $_";
$len = $word_len;
}
else {
$str .= " $_";
$len += $word_len + 1;
}
}
return "- " . $str;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# seconds = raw_date_to_s(git_date)
#
# Convert git raw date to seconds.
# - git_date: raw git format (e.g. "1443184889 +0200")
# - seconds: the seconds part (e.g. "1443184889")
#
sub raw_date_to_s
{
return (split / /, $_[0])[0];
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# date = format_date_obs(git_date)
#
# Convert git raw date to obs format.
# - git_date: raw git format (e.g. "1443184889 +0200")
# - date: obs format ("Fri Sep 25 12:41:29 UTC 2015")
#
sub format_date_obs
{
my @d = gmtime(raw_date_to_s($_[0]));
return
qw(Sun Mon Tue Wed Thu Fri Sat)[$d[6]] . " " .
qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$d[4]] . " " .
$d[3] . " " .
sprintf("%02d:%02d:%02d", $d[2], $d[1], $d[0]) . " UTC " .
(1900 + $d[5]);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# date = format_date_iso(git_date)
#
# Convert git raw date to iso format.
# - git_date: raw git format (e.g. "1443184889 +0200")
# - date: obs format ("2015-09-25")
#
sub format_date_iso
{
my @d = gmtime(raw_date_to_s($_[0]));
return sprintf("%04d-%02d-%02d", 1900 + $d[5], $d[4] + 1, $d[3]);
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_openSUSE/linuxrc.git
git@gitee.com:mirrors_openSUSE/linuxrc.git
mirrors_openSUSE
linuxrc
linuxrc
master

搜索帮助