our
$VERSION
= version->new(1.1.20);
our
$workflow
= App::Git::Workflow->new;
our
(
$name
) =
$PROGRAM_NAME
=~ m{^.*/(.*?)$}mxs;
our
%option
;
our
$memoized
= 0;
sub
run {
my
$self
=
shift
;
get_options(
\
%option
,
'all|a'
,
'branch|b'
,
'day|d'
,
'depth|D=i'
,
'path_depth|path-depth|p=i%'
,
'files|f'
,
'ignore_files|ignore-files=s@'
,
'ignore_user|ignore-users=s@'
,
'ignore_branch|ignore-branches=s@'
,
'month|m'
,
'out|o=s'
,
'quiet|q'
,
'remote|r'
,
'since|s=s'
,
'tag|t'
,
'users|u'
,
'week|w'
,
);
if
(!
$memoized
) {
my
$git_dir
=
$workflow
->git->rev_parse(
"--show-toplevel"
);
chomp
$git_dir
;
$git_dir
=~ s{[/\\]$}{};
memoize(
'App::Git::Workflow::commit_details'
,
driver
=>
'File'
,
root_dir
=>
"$git_dir/.git/gw-commit-detials"
,
expires_in
=>
'1M'
,
key
=>
sub
{
shift
@_
;
@_
},
);
$memoized
= 1;
}
my
@commits
=
$self
->recent_commits(\
%option
);
my
%changed
=
$self
->changed_from_shas(
@commits
);
if
(
$option
{users} ) {
my
%users
;
for
my
$file
(
keys
%changed
) {
for
my
$user
(@{
$changed
{
$file
}{users} }) {
$users
{
$user
} ||= {};
@{
$users
{
$user
}{files} } = (
uniq
sort
@{
$users
{
$user
}{files} || [] }, @{
$changed
{
$file
}{files} || [] }
);
@{
$users
{
$user
}{branches} } = (
uniq
sort
@{
$users
{
$user
}{branches} || [] }, @{
$changed
{
$file
}{branches} || [] }
);
}
}
%changed
=
%users
;
}
elsif
(
$option
{branches} ) {
my
%branches
;
for
my
$file
(
keys
%changed
) {
for
my
$branch
(@{
$changed
{
$file
}{branches} }) {
$branches
{
$branch
} ||= {};
@{
$branches
{
$branch
}{files} } = (
uniq
sort
@{
$branches
{
$branch
}{files} || [] }, @{
$changed
{
$file
}{files} || [] }
);
@{
$branches
{
$branch
}{users} } = (
uniq
sort
@{
$branches
{
$branch
}{users} || [] }, @{
$changed
{
$file
}{users} || [] }
);
}
}
%changed
=
%branches
;
}
else
{
my
%files
;
for
my
$file
(
keys
%changed
) {
delete
$changed
{
$file
}{files};
}
}
my
$out
=
'out_'
. (
$option
{out} ||
'text'
);
if
(
$self
->can(
$out
)) {
$self
->
$out
(\
%changed
);
}
return
;
}
sub
out_text {
my
(
$self
,
$changed
) =
@_
;
for
my
$file
(
sort
keys
%$changed
) {
print
"$file\n"
;
if
( !
$option
{users} ) {
print
" Changed by : "
. (
join
', '
, @{
$changed
->{
$file
}{users} || [] } ),
"\n"
;
}
if
( !
$option
{branches} ) {
print
" In branches: "
. (
join
', '
, @{
$changed
->{
$file
}{branches} || [] } ),
"\n"
;
}
if
(
$option
{users} ||
$option
{branches} ) {
print
" Files: "
. (
join
', '
, @{
$changed
->{
$file
}{files} || [] } ),
"\n"
;
}
}
return
;
}
sub
out_perl {
my
(
$self
,
$changed
) =
@_
;
$Data::Dumper::Sortkeys
= 1;
$Data::Dumper::Indent
= 1;
print
Dumper
$changed
;
return
;
}
sub
out_json {
my
(
$self
,
$changed
) =
@_
;
print
JSON::encode_json(
$changed
),
"\n"
;
return
;
}
sub
out_yaml {
my
(
$self
,
$changed
) =
@_
;
print
YAML::Dump(
$changed
);
return
;
}
sub
recent_commits {
my
(
$self
,
$option
) =
@_
;
my
@args
= (
'--since'
,
$option
->{since} );
if
( !
$option
->{since} ) {
my
$sec_ago
=
$option
->{month} ? 60 * 60 * 24 * 30
:
$option
->{week} ? 60 * 60 * 24 * 7
: 60 * 60 * 24;
my
(
undef
,
undef
,
undef
,
$day
,
$month
,
$year
) =
localtime
(
time
-
$sec_ago
);
$year
+= 1900;
$month
++;
@args
= (
'--since'
,
sprintf
"%04d-%02d-%02d"
,
$year
,
$month
,
$day
);
}
unshift
@args
,
$option
->{tag} ?
'--tags'
:
$option
->{all} ?
'--all'
:
$option
->{remote} ?
'--remotes'
:
'--branches'
;
return
$workflow
->git->rev_list(
@args
);
}
sub
changed_from_shas {
my
(
$self
,
@commits
) =
@_
;
my
%changed
;
my
$count
= 0;
print
{
*STDERR
}
'.'
if
$option
{verbose};
for
my
$sha
(
@commits
) {
my
$changed
=
$workflow
->commit_details(
$sha
,
branches
=> 1,
files
=> 1,
user
=> 1);
next
if
$self
->ignore(
$changed
);
for
my
$type
(
keys
%{
$changed
->{files} }) {
if
(
defined
$option
{depth} ) {
$type
=
join
'/'
,
grep
{
defined
$_
} (
split
m{/},
$type
)[0 ..
$option
{depth} - 1];
}
if
(
defined
$option
{path_depth} ) {
for
my
$path
(
keys
%{
$option
{path_depth} }) {
if
(
$type
=~ /^
$path
/ ) {
$type
=
join
'/'
,
grep
{
defined
$_
} (
split
m{/},
$type
)[0 ..
$option
{path_depth}{
$path
} - 1];
}
}
}
my
%branches
;
if
(
$option
{remote} ) {
%branches
=
map
{
$_
=> 1 }
grep
{/^origin/}
keys
%{
$changed
->{branches} };
}
elsif
(
$option
{all} ) {
%branches
= %{
$changed
->{branches} };
}
else
{
%branches
=
map
{
$_
=> 1 }
grep
{!/^origin/}
keys
%{
$changed
->{branches} };
}
next
if
!
%branches
;
$changed
{
$type
}{users}{
$changed
->{user}}++;
$changed
{
$type
}{files} = {
%{
$changed
{
$type
}{files} || {} },
%{
$changed
->{files} },
};
$changed
{
$type
}{branches} = {
%{
$changed
{
$type
}{branches} || {} },
%branches
,
};
}
print
{
*STDERR
}
'.'
if
$option
{verbose} && ++
$count
% 10 == 0;
}
for
my
$type
(
keys
%changed
) {
$changed
{
$type
}{users } = [
sort
keys
%{
$changed
{
$type
}{users } } ];
$changed
{
$type
}{files } = [
sort
keys
%{
$changed
{
$type
}{files } } ];
$changed
{
$type
}{branches} = [
sort
keys
%{
$changed
{
$type
}{branches} } ];
}
return
%changed
;
}
sub
ignore {
my
(
$self
,
$commit
) =
@_
;
if
(
$option
{ignore_files}) {
for
my
$ignore
(@{
$option
{ignore_files} }) {
for
my
$file
(
keys
%{
$commit
->{files} }) {
return
1
if
$file
=~ /
$ignore
/;
}
}
}
if
(
$option
{ignore_user}
&&
grep
{
$commit
->{user} =~ /
$_
/} @{
$option
{ignore_user} } ) {
return
1;
}
if
(
$option
{ignore_branch}
&&
grep
{
$commit
->{branches} =~ /
$_
/} @{
$option
{ignore_branch} } ) {
return
1;
}
return
0;
}
1;