]> Dogcows Code - chaz/git-codeowners/commitdiff
Release 0.42 solo-0.42
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Wed, 13 Nov 2019 04:53:15 +0000 (21:53 -0700)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Wed, 13 Nov 2019 04:53:15 +0000 (21:53 -0700)
README
git-codeowners

diff --git a/README b/README
index a5e5712b2790ec9ac9530476265e44cfb468ec85..98bbc5b1692973b64e2f6f65a76fc27138632b22 100644 (file)
--- a/README
+++ b/README
@@ -4,13 +4,15 @@ NAME
 
 VERSION
 
-    version 0.41
+    version 0.42
 
 SYNOPSIS
 
         git-codeowners [--version|--help|--manual]
     
-        git-codeowners [show] [--format FORMAT] [--[no-]project] [PATH...]
+        git-codeowners [show] [--format FORMAT] [--owner OWNER]...
+                       [--pattern PATTERN]... [--[no-]patterns]
+                       [--project PROJECT]... [--[no-]projects] [PATH...]
     
         git-codeowners owners [--format FORMAT] [--pattern PATTERN]
     
@@ -100,18 +102,35 @@ COMMANDS
 
  show
 
-        git-codeowners [show] [--format FORMAT] [--[no-]project] [PATH...]
+        git-codeowners [show] [--format FORMAT] [--owner OWNER]...
+                       [--pattern PATTERN]... [--[no-]patterns]
+                       [--project PROJECT]... [--[no-]projects] [PATH...]
 
     Show owners of one or more files in a repo.
 
+    If --owner, --project, --pattern are set, only show files with matching
+    criteria. These can be repeated.
+
+    Use --patterns to also show the matching pattern associated with each
+    file.
+
+    By default the output might show associated projects if the CODEOWNERS
+    file defines them. You can control this by explicitly using --projects
+    or --no-projects to always show or always hide defined projects,
+    respectively.
+
  owners
 
         git-codeowners owners [--format FORMAT] [--pattern PATTERN]
 
+    List all owners defined in the CODEOWNERS file.
+
  patterns
 
         git-codeowners patterns [--format FORMAT] [--owner OWNER]
 
+    List all patterns defined in the CODEOWNERS file.
+
  create
 
         git-codeowners create [REPO_DIRPATH|CODEOWNERS_FILEPATH]
@@ -144,7 +163,7 @@ FORMAT
 
       * FORMAT - Custom format (see below)
 
- Custom
+ Format string
 
     You can specify a custom format using printf-like format sequences.
     These are the items that can be substituted:
@@ -177,7 +196,7 @@ FORMAT
 
       * nocolor - Do not colorize replacement string.
 
Table
Format table
 
     Table formatting can be done by one of several different modules, each
     with its own features and bugs. The default module is
@@ -188,6 +207,10 @@ FORMAT
 
     The list of available modules is at "@BACKENDS" in Text::Table::Any.
 
+CAVEATS
+
+      * Some commands require git (at least version 1.8.5).
+
 BUGS
 
     Please report any bugs or feature requests on the bugtracker website
index 8a46cb2354e6f3ef491f414cb0c8c9ed978e6614..e0575aab0f4f30f3ef0b6c9c424bc003be4196c8 100755 (executable)
@@ -10,7 +10,7 @@ BEGIN {
 my %fatpacked;
 
 $fatpacked{"App/Codeowners.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS';
-  package App::Codeowners;use v5.10.1;use utf8;use warnings;use strict;use App::Codeowners::Options;use App::Codeowners::Util qw(find_codeowners_in_directory run_git git_ls_files git_toplevel stringf);use Color::ANSI::Util qw(ansifg ansi_reset);use Encode qw(encode);use File::Codeowners;use Path::Tiny;our$VERSION='0.41';sub main {my$class=shift;my$self=bless {},$class;my$opts=App::Codeowners::Options->new(@_);my$color=$opts->{color};local$ENV{NO_COLOR}=1 if defined$color &&!$color;my$command=$opts->command;my$handler=$self->can("_command_$command")or die "Unknown command: $command\n";$self->$handler($opts);exit 0}sub _command_show {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my ($cdup)=run_git(qw{rev-parse --show-cdup});my@results;my$filepaths=git_ls_files('.',$opts->args)or die "Cannot list files\n";for my$filepath (@$filepaths){my$match=$codeowners->match(path($filepath)->relative($cdup));push@results,[$filepath,$match->{owners},$opts->{project}? $match->{project}: (),]}_format(format=>$opts->{format}|| ' * %-50F %O',out=>*STDOUT,headers=>[qw(File Owner),$opts->{project}? 'Project' : ()],rows=>\@results,)}sub _command_owners {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my$results=$codeowners->owners($opts->{pattern});_format(format=>$opts->{format}|| '%O',out=>*STDOUT,headers=>[qw(Owner)],rows=>[map {[$_]}@$results],)}sub _command_patterns {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my$results=$codeowners->patterns($opts->{owner});_format(format=>$opts->{format}|| '%T',out=>*STDOUT,headers=>[qw(Pattern)],rows=>[map {[$_]}@$results],)}sub _command_create {goto&_command_update}sub _command_update {my$self=shift;my$opts=shift;my ($filepath)=$opts->args;my$path=path($filepath || '.');my$repopath;die "Does not exist: $path\n" if!$path->parent->exists;if ($path->is_dir){$repopath=$path;$path=find_codeowners_in_directory($path)|| $repopath->child('CODEOWNERS')}my$is_new=!$path->is_file;my$codeowners;if ($is_new){$codeowners=File::Codeowners->new;my$template=<<'END';for my$line (split(/\n/,$template)){$codeowners->append(comment=>$line)}}else {$codeowners=File::Codeowners->parse_from_filepath($path)}if ($repopath){my$git_files=git_ls_files($repopath);if (@$git_files){$codeowners->clear_unowned;$codeowners->add_unowned(grep {!$codeowners->match($_)}@$git_files)}}$codeowners->write_to_filepath($path);print STDERR "Wrote $path\n"}sub _format {my%args=@_;my$format=$args{format}|| 'table';my$fh=$args{out}|| *STDOUT;my$headers=$args{headers}|| [];my$rows=$args{rows}|| [];if ($format eq 'table'){eval {require Text::Table::Any}or die "Missing dependency: Text::Table::Any\n";my$table=Text::Table::Any::table(header_row=>1,rows=>[$headers,map {[map {_stringify($_)}@$_]}@$rows],backend=>$ENV{PERL_TEXT_TABLE},);print {$fh}encode('UTF-8',$table)}elsif ($format =~ /^json(:pretty)?$/){my$pretty=!!$1;eval {require JSON::MaybeXS}or die "Missing dependency: JSON::MaybeXS\n";my$json=JSON::MaybeXS->new(canonical=>1,utf8=>1,pretty=>$pretty);my$data=_combine_headers_rows($headers,$rows);print {$fh}$json->encode($data)}elsif ($format =~ /^([ct])sv$/){my$sep=$1 eq 'c' ? ',' : "\t";eval {require Text::CSV}or die "Missing dependency: Text::CSV\n";my$csv=Text::CSV->new({binary=>1,eol=>$/,sep=>$sep});$csv->print($fh,$headers);$csv->print($fh,[map {encode('UTF-8',_stringify($_))}@$_])for @$rows}elsif ($format =~ /^ya?ml$/){eval {require YAML}or die "Missing dependency: YAML\n";my$data=_combine_headers_rows($headers,$rows);print {$fh}encode('UTF-8',YAML::Dump($data))}else {my$data=_combine_headers_rows($headers,$rows);my@contrasting_colors=qw(e6194b 3cb44b ffe119 4363d8 f58231 911eb4 42d4f4 f032e6 bfef45 fabebe 469990 e6beff 9a6324 fffac8 800000 aaffc3 808000 ffd8b1 000075 a9a9a9);my%owner_colors;my$num=-1;my$owner_color=sub {my$owner=shift or return;$owner_colors{$owner}||= do {$num=($num + 1)% scalar@contrasting_colors;$contrasting_colors[$num]}};my%filter=(quote=>sub {local $_=$_[0];s/"/\"/s;"\"$_\""},);my$create_filterer=sub {my$value=shift || '';my$color=shift || '';my$gencolor=ref($color)eq 'CODE' ? $color : sub {$color};return sub {my$arg=shift;my ($filters,$color)=_expand_filter_args($arg);if (ref($value)eq 'ARRAY'){$value=join(',',map {_colored($_,$color // $gencolor->($_))}@$value)}else {$value=_colored($value,$color // $gencolor->($value))}for my$key (@$filters){if (my$filter=$filter{$key}){$value=$filter->($value)}else {warn "Unknown filter: $key\n"}}$value || ''}};for my$row (@$data){my%info=(F=>$create_filterer->($row->{File},undef),O=>$create_filterer->($row->{Owner},$owner_color),P=>$create_filterer->($row->{Project},undef),T=>$create_filterer->($row->{Pattern},undef),);my$text=stringf($format,%info);print {$fh}encode('UTF-8',$text),"\n"}}}sub _expand_filter_args {my$arg=shift || '';my@filters=split(/,/,$arg);my$color_override;for (my$i=0;$i < @filters;++$i){my$filter=$filters[$i]or next;if ($filter =~ /^(?:nocolor|color:([0-9a-fA-F]{3,6}))$/){$color_override=$1 || '';splice(@filters,$i,1);redo}}return (\@filters,$color_override)}sub _colored {my$text=shift;my$rgb=shift or return$text;return$text if$ENV{NO_COLOR};$rgb =~ s/^(.)(.)(.)$/$1$1$2$2$3$3/;if ($rgb !~ m/^[0-9a-fA-F]{6}$/){warn "Color value must be in 'ffffff' or 'fff' form.\n";return$text}my ($begin,$end)=(ansifg($rgb),ansi_reset);return "${begin}${text}${end}"}sub _combine_headers_rows {my$headers=shift;my$rows=shift;my@new_rows;for my$row (@$rows){push@new_rows,(my$new_row={});for (my$i=0;$i < @$headers;++$i){$new_row->{$headers->[$i]}=$row->[$i]}}return \@new_rows}sub _stringify {my$item=shift;return ref($item)eq 'ARRAY' ? join(',',@$item): $item}1;
+  package App::Codeowners;use v5.10.1;use utf8;use warnings;use strict;use App::Codeowners::Formatter;use App::Codeowners::Options;use App::Codeowners::Util qw(find_codeowners_in_directory run_git git_ls_files git_toplevel);use Color::ANSI::Util 0.03 qw(ansifg);use Encode qw(encode);use File::Codeowners;use Path::Tiny;our$VERSION='0.42';sub main {my$class=shift;my$self=bless {},$class;my$opts=App::Codeowners::Options->new(@_);my$color=$opts->{color};local$ENV{NO_COLOR}=1 if defined$color &&!$color;my$command=$opts->command;my$handler=$self->can("_command_$command")or die "Unknown command: $command\n";$self->$handler($opts);exit 0}sub _command_show {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my ($proc,$cdup)=run_git(qw{rev-parse --show-cdup});$proc->wait and exit 1;my$show_projects=$opts->{projects}// scalar @{$codeowners->projects};my$formatter=App::Codeowners::Formatter->new(format=>$opts->{format}|| ' * %-50F %O',handle=>*STDOUT,columns=>['File',$opts->{patterns}? 'Pattern' : (),'Owner',$show_projects ? 'Project' : (),],);my%filter_owners=map {$_=>1}@{$opts->{owner}};my%filter_projects=map {$_=>1}@{$opts->{project}};my%filter_patterns=map {$_=>1}@{$opts->{pattern}};$proc=git_ls_files('.',$opts->args);while (my$filepath=$proc->next){my$match=$codeowners->match(path($filepath)->relative($cdup));if (%filter_owners){for my$owner (@{$match->{owners}}){goto ADD_RESULT if$filter_owners{$owner}}next}if (%filter_patterns){goto ADD_RESULT if$filter_patterns{$match->{pattern}|| ''};next}if (%filter_projects){goto ADD_RESULT if$filter_projects{$match->{project}|| ''};next}ADD_RESULT: $formatter->add_result([$filepath,$opts->{patterns}? $match->{pattern}: (),$match->{owners},$show_projects ? $match->{project}: (),])}$proc->wait and exit 1}sub _command_owners {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my$results=$codeowners->owners($opts->{pattern});my$formatter=App::Codeowners::Formatter->new(format=>$opts->{format}|| '%O',handle=>*STDOUT,columns=>[qw(Owner)],);$formatter->add_result(map {[$_]}@$results)}sub _command_patterns {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my$results=$codeowners->patterns($opts->{owner});my$formatter=App::Codeowners::Formatter->new(format=>$opts->{format}|| '%T',handle=>*STDOUT,columns=>[qw(Pattern)],);$formatter->add_result(map {[$_]}@$results)}sub _command_projects {my$self=shift;my$opts=shift;my$toplevel=git_toplevel('.')or die "Not a git repo\n";my$codeowners_path=find_codeowners_in_directory($toplevel)or die "No CODEOWNERS file in $toplevel\n";my$codeowners=File::Codeowners->parse_from_filepath($codeowners_path);my$results=$codeowners->projects;my$formatter=App::Codeowners::Formatter->new(format=>$opts->{format}|| '%P',handle=>*STDOUT,columns=>[qw(Project)],);$formatter->add_result(map {[$_]}@$results)}sub _command_create {goto&_command_update}sub _command_update {my$self=shift;my$opts=shift;my ($filepath)=$opts->args;my$path=path($filepath || '.');my$repopath;die "Does not exist: $path\n" if!$path->parent->exists;if ($path->is_dir){$repopath=$path;$path=find_codeowners_in_directory($path)|| $repopath->child('CODEOWNERS')}my$is_new=!$path->is_file;my$codeowners;if ($is_new){$codeowners=File::Codeowners->new;my$template=<<'END';for my$line (split(/\n/,$template)){$codeowners->append(comment=>$line)}}else {$codeowners=File::Codeowners->parse_from_filepath($path)}if ($repopath){my$git_files=git_ls_files($repopath);if (@$git_files){$codeowners->clear_unowned;$codeowners->add_unowned(grep {!$codeowners->match($_)}@$git_files)}}$codeowners->write_to_filepath($path);print STDERR "Wrote $path\n"}1;
    This file shows mappings between subdirs/files and the individuals and
    teams who own them. You can read this file yourself or use tools to query it,
    so you can quickly determine who to speak with or send pull requests to. ❤️
@@ -22,8 +22,36 @@ $fatpacked{"App/Codeowners.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'
   END
 APP_CODEOWNERS
 
+$fatpacked{"App/Codeowners/Formatter.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER';
+  package App::Codeowners::Formatter;use warnings;use strict;our$VERSION='0.42';use Module::Load;sub new {my$class=shift;my$args={@_==1 && ref $_[0]eq 'HASH' ? %{$_[0]}: @_};$args->{results}=[];($class,my$format)=$class->_best_formatter($args->{format})if$args->{format};$args->{format}=$format;my$self=bless$args,$class;$self->start;return$self}sub _best_formatter {my$class=shift;my$type=shift || '';return ($class,$type)if$class ne __PACKAGE__;my ($name,$format)=$type =~ /^([A-Za-z]+)(?::(.*))?$/;if (!$name){$name='';$format=''}$name=lc($name);$name =~ s/:.*//;my@formatters=$class->formatters;my$package=__PACKAGE__.'::String';for my$formatter (@formatters){my$module=lc($formatter);$module =~ s/.*:://;if ($module eq $name){$package=$formatter;$type=$format;last}}load$package;return ($package,$type)}sub DESTROY {my$self=shift;my$global_destruction=shift;return if$global_destruction;my$results=$self->{results};$self->finish($results)if$results;delete$self->{results}}sub handle {shift->{handle}}sub format {shift->{format}|| ''}sub columns {shift->{columns}|| []}sub results {shift->{results}}sub add_result {my$self=shift;$self->stream($_)for @_}sub start {}sub stream {push @{$_[0]->results},$_[1]}sub finish {}sub formatters {return qw(App::Codeowners::Formatter::CSV App::Codeowners::Formatter::JSON App::Codeowners::Formatter::String App::Codeowners::Formatter::TSV App::Codeowners::Formatter::Table App::Codeowners::Formatter::YAML)}1;
+APP_CODEOWNERS_FORMATTER
+
+$fatpacked{"App/Codeowners/Formatter/CSV.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER_CSV';
+  package App::Codeowners::Formatter::CSV;use warnings;use strict;our$VERSION='0.42';use parent 'App::Codeowners::Formatter';use App::Codeowners::Util qw(stringify);use Encode qw(encode);sub start {my$self=shift;$self->text_csv->print($self->handle,$self->columns)}sub stream {my$self=shift;my$result=shift;$self->text_csv->print($self->handle,[map {encode('UTF-8',stringify($_))}@$result])}sub text_csv {my$self=shift;$self->{text_csv}||= do {eval {require Text::CSV}or die "Missing dependency: Text::CSV\n";my%options;$options{escape_char}=$self->escape_char if$self->escape_char;$options{quote}=$self->quote if$self->quote;$options{sep}=$self->sep if$self->sep;if ($options{sep}&& $options{sep}eq ($options{quote}|| '"')){die "Invalid separator value for CSV format.\n"}Text::CSV->new({binary=>1,eol=>$/,%options})}or die "Failed to construct Text::CSV object"}sub sep {$_[0]->{sep}|| $_[0]->format}sub quote {$_[0]->{quote}}sub escape_char {$_[0]->{escape_char}}1;
+APP_CODEOWNERS_FORMATTER_CSV
+
+$fatpacked{"App/Codeowners/Formatter/JSON.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER_JSON';
+  package App::Codeowners::Formatter::JSON;use warnings;use strict;our$VERSION='0.42';use parent 'App::Codeowners::Formatter';use App::Codeowners::Util qw(zip);sub finish {my$self=shift;my$results=shift;eval {require JSON::MaybeXS}or die "Missing dependency: JSON::MaybeXS\n";my%options;$options{pretty}=1 if lc($self->format)eq 'pretty';my$json=JSON::MaybeXS->new(canonical=>1,utf8=>1,%options);my$columns=$self->columns;$results=[map {+{zip @$columns,@$_}}@$results];print {$self->handle}$json->encode($results)}1;
+APP_CODEOWNERS_FORMATTER_JSON
+
+$fatpacked{"App/Codeowners/Formatter/String.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER_STRING';
+  package App::Codeowners::Formatter::String;use warnings;use strict;our$VERSION='0.42';use parent 'App::Codeowners::Formatter';use App::Codeowners::Util qw(stringf zip);use Color::ANSI::Util 0.03 qw(ansifg);use Encode qw(encode);sub stream {my$self=shift;my$result=shift;$result={zip @{$self->columns},@$result};my%info=(F=>$self->_create_filterer->($result->{File},undef),O=>$self->_create_filterer->($result->{Owner},$self->_owner_colorgen),P=>$self->_create_filterer->($result->{Project},undef),T=>$self->_create_filterer->($result->{Pattern},undef),);my$text=stringf($self->format,%info);print {$self->handle}encode('UTF-8',$text),"\n"}sub _expand_filter_args {my$arg=shift || '';my@filters=split(/,/,$arg);my$color_override;for (my$i=0;$i < @filters;++$i){my$filter=$filters[$i]or next;if ($filter =~ /^(?:nocolor|color:([0-9a-fA-F]{3,6}))$/){$color_override=$1 || '';splice(@filters,$i,1);redo}}return (\@filters,$color_override)}sub _ansi_reset {"\033[0m"}sub _colored {my$text=shift;my$rgb=shift or return$text;return$text if$ENV{NO_COLOR};$rgb =~ s/^(.)(.)(.)$/$1$1$2$2$3$3/;if ($rgb !~ m/^[0-9a-fA-F]{6}$/){warn "Color value must be in 'ffffff' or 'fff' form.\n";return$text}my ($begin,$end)=(ansifg($rgb),_ansi_reset);return "${begin}${text}${end}"}sub _create_filterer {my$self=shift;my%filter=(quote=>sub {local $_=$_[0];s/"/\"/s;"\"$_\""},);return sub {my$value=shift || '';my$color=shift || '';my$gencolor=ref($color)eq 'CODE' ? $color : sub {$color};return sub {my$arg=shift;my ($filters,$color)=_expand_filter_args($arg);if (ref($value)eq 'ARRAY'){$value=join(',',map {_colored($_,$color // $gencolor->($_))}@$value)}else {$value=_colored($value,$color // $gencolor->($value))}for my$key (@$filters){if (my$filter=$filter{$key}){$value=$filter->($value)}else {warn "Unknown filter: $key\n"}}$value || ''}}}sub _owner_colorgen {my$self=shift;my@contrasting_colors=qw(e6194b 3cb44b ffe119 4363d8 f58231 911eb4 42d4f4 f032e6 bfef45 fabebe 469990 e6beff 9a6324 fffac8 800000 aaffc3 808000 ffd8b1 000075 a9a9a9);my%owner_colors;my$num=-1;$self->{owner_color}||= sub {my$owner=shift or return;$owner_colors{$owner}||= do {$num=($num + 1)% scalar@contrasting_colors;$contrasting_colors[$num]}}}1;
+APP_CODEOWNERS_FORMATTER_STRING
+
+$fatpacked{"App/Codeowners/Formatter/TSV.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER_TSV';
+  package App::Codeowners::Formatter::TSV;use warnings;use strict;our$VERSION='0.42';use parent 'App::Codeowners::Formatter::CSV';sub sep {"\t"}1;
+APP_CODEOWNERS_FORMATTER_TSV
+
+$fatpacked{"App/Codeowners/Formatter/Table.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER_TABLE';
+  package App::Codeowners::Formatter::Table;use warnings;use strict;our$VERSION='0.42';use parent 'App::Codeowners::Formatter';use App::Codeowners::Util qw(stringify);use Encode qw(encode);sub finish {my$self=shift;my$results=shift;eval {require Text::Table::Any}or die "Missing dependency: Text::Table::Any\n";my$table=Text::Table::Any::table(header_row=>1,rows=>[$self->columns,map {[map {stringify($_)}@$_]}@$results],backend=>$ENV{PERL_TEXT_TABLE},);print {$self->handle}encode('UTF-8',$table)}1;
+APP_CODEOWNERS_FORMATTER_TABLE
+
+$fatpacked{"App/Codeowners/Formatter/YAML.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_FORMATTER_YAML';
+  package App::Codeowners::Formatter::YAML;use warnings;use strict;our$VERSION='0.42';use parent 'App::Codeowners::Formatter';use App::Codeowners::Util qw(zip);sub finish {my$self=shift;my$results=shift;eval {require YAML}or die "Missing dependency: YAML\n";my$columns=$self->columns;$results=[map {+{zip @$columns,@$_}}@$results];print {$self->handle}YAML::Dump($results)}1;
+APP_CODEOWNERS_FORMATTER_YAML
+
 $fatpacked{"App/Codeowners/Options.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_OPTIONS';
-  package App::Codeowners::Options;use warnings;use strict;use Getopt::Long 2.39 ();use Path::Tiny;use Pod::Usage;our$VERSION='0.41';sub early_options {return {'color|colour!'=>(-t STDOUT ? 1 : 0),'format|f=s'=>undef,'help|h|?'=>0,'manual|man'=>0,'shell-completion:s'=>undef,'version|v'=>0,}}sub command_options {return {'create'=>{},'owners'=>{'pattern=s'=>'',},'patterns'=>{'owner=s'=>'',},'show'=>{'project!'=>1,},'update'=>{},}}sub commands {my$self=shift;my@commands=sort keys %{$self->command_options};return@commands}sub options {my$self=shift;my@command_options;if (my$command=$self->{command}){@command_options=keys %{$self->command_options->{$command}|| {}}}return (keys %{$self->early_options},@command_options)}sub new {my$class=shift;my@args=@_;my$self=bless {},$class;my@args_copy=@args;my$opts=$self->get_options(args=>\@args,spec=>$self->early_options,config=>'pass_through',)or pod2usage(2);if ($ENV{CODEOWNERS_COMPLETIONS}){$self->{command}=$args[0]|| '';my$cword=$ENV{CWORD};my$cur=$ENV{CUR}|| '';while (0 < --$cword){last if$cur eq ($args_copy[$cword]|| '')}$self->completions($cword,@args_copy);exit 0}if ($opts->{version}){my$progname=path($0)->basename;print "${progname} ${VERSION}\n";exit 0}if ($opts->{help}){pod2usage(-exitval=>0,-verbose=>99,-sections=>[qw(NAME SYNOPSIS OPTIONS)])}if ($opts->{manual}){pod2usage(-exitval=>0,-verbose=>2)}if (defined$opts->{shell_completion}){$self->shell_completion($opts->{shell_completion});exit 0}my$command=shift@args;my$command_options=$self->command_options->{$command || ''};if (!$command_options){unshift@args,$command if defined$command;$command='show';$command_options=$self->command_options->{$command}}my$more_opts=$self->get_options(args=>\@args,spec=>$command_options,)or pod2usage(2);%$self=(%$opts,%$more_opts,command=>$command,args=>\@args);return$self}sub command {my$self=shift;my$command=$self->{command};my@commands=sort keys %{$self->command_options};return if not grep {$_ eq $command}@commands;$command =~ s/[^a-z]/_/g;return$command}sub args {my$self=shift;return @{$self->{args}|| []}}sub get_options {my$self=shift;my$args={@_==1 && ref $_[0]eq 'HASH' ? %{$_[0]}: @_};my%options;my%results;while (my ($opt,$default_value)=each %{$args->{spec}}){my ($name)=$opt =~ /^([^=:!|]+)/;$name =~ s/-/_/g;$results{$name}=$default_value;$options{$opt}=\$results{$name}}if (my$fn=$args->{callback}){$options{'<>'}=sub {my$arg=shift;$fn->($arg,\%results)}}my$p=Getopt::Long::Parser->new;$p->configure($args->{config}|| 'default');return if!$p->getoptionsfromarray($args->{args},%options);return \%results}sub shell_completion {my$self=shift;my$type=lc(shift || 'bash');if ($type eq 'bash'){print <<'END'}else {warn "No such shell completion: $type\n"}}sub completions {my$self=shift;my$cword=shift;my@words=@_;my$current=$words[$cword]|| '';my$prev=$words[$cword - 1]|| '';my$reply;if ($prev eq '--format' || $prev eq '-f'){$reply=$self->_completion_formats}elsif ($current =~ /^-/){$reply=$self->_completion_options}else {if (!$self->command){$reply=[$self->commands,@{$self->_completion_options([keys %{$self->early_options}])}]}else {print 'file';exit 9}}local $,="\n";print grep {/^\Q$current\E/}@$reply;exit 0}sub _completion_options {my$self=shift;my$opts=shift || [$self->options];my@options;for my$option (@$opts){my ($names,$op,$vtype)=$option =~ /^([^=:!]+)([=:!]?)(.*)$/;my@names=split(/\|/,$names);for my$name (@names){if ($op eq '!'){push@options,"--$name","--no-$name"}else {if (length($name)> 1){push@options,"--$name"}else {push@options,"-$name"}}}}return [sort@options]}sub _completion_formats {[qw(csv json json:pretty tsv yaml)]}1;
+  package App::Codeowners::Options;use warnings;use strict;use Getopt::Long 2.39 ();use Path::Tiny;use Pod::Usage;our$VERSION='0.42';sub early_options {return {'color|colour!'=>(-t STDOUT ? 1 : 0),'format|f=s'=>undef,'help|h|?'=>0,'manual|man'=>0,'shell-completion:s'=>undef,'version|v'=>0,}}sub command_options {return {'create'=>{},'owners'=>{'pattern=s'=>'',},'patterns'=>{'owner=s'=>'',},'projects'=>{},'show'=>{'owner=s@'=>[],'pattern=s@'=>[],'project=s@'=>[],'patterns!'=>0,'projects!'=>undef,},'update'=>{},}}sub commands {my$self=shift;my@commands=sort keys %{$self->command_options};return@commands}sub options {my$self=shift;my@command_options;if (my$command=$self->{command}){@command_options=keys %{$self->command_options->{$command}|| {}}}return (keys %{$self->early_options},@command_options)}sub new {my$class=shift;my@args=@_;my$self=bless {},$class;my@args_copy=@args;my$opts=$self->get_options(args=>\@args,spec=>$self->early_options,config=>'pass_through',)or pod2usage(2);if ($ENV{CODEOWNERS_COMPLETIONS}){$self->{command}=$args[0]|| '';my$cword=$ENV{CWORD};my$cur=$ENV{CUR}|| '';while (0 < --$cword){last if$cur eq ($args_copy[$cword]|| '')}$self->completions($cword,@args_copy);exit 0}if ($opts->{version}){my$progname=path($0)->basename;print "${progname} ${VERSION}\n";exit 0}if ($opts->{help}){pod2usage(-exitval=>0,-verbose=>99,-sections=>[qw(NAME SYNOPSIS OPTIONS COMMANDS)])}if ($opts->{manual}){pod2usage(-exitval=>0,-verbose=>2)}if (defined$opts->{shell_completion}){$self->shell_completion($opts->{shell_completion});exit 0}my$command=shift@args;my$command_options=$self->command_options->{$command || ''};if (!$command_options){unshift@args,$command if defined$command;$command='show';$command_options=$self->command_options->{$command}}my$more_opts=$self->get_options(args=>\@args,spec=>$command_options,)or pod2usage(2);%$self=(%$opts,%$more_opts,command=>$command,args=>\@args);return$self}sub command {my$self=shift;my$command=$self->{command};my@commands=sort keys %{$self->command_options};return if not grep {$_ eq $command}@commands;$command =~ s/[^a-z]/_/g;return$command}sub args {my$self=shift;return @{$self->{args}|| []}}sub get_options {my$self=shift;my$args={@_==1 && ref $_[0]eq 'HASH' ? %{$_[0]}: @_};my%options;my%results;while (my ($opt,$default_value)=each %{$args->{spec}}){my ($name)=$opt =~ /^([^=:!|]+)/;$name =~ s/-/_/g;$results{$name}=$default_value;$options{$opt}=\$results{$name}}if (my$fn=$args->{callback}){$options{'<>'}=sub {my$arg=shift;$fn->($arg,\%results)}}my$p=Getopt::Long::Parser->new;$p->configure($args->{config}|| 'default');return if!$p->getoptionsfromarray($args->{args},%options);return \%results}sub shell_completion {my$self=shift;my$type=lc(shift || 'bash');if ($type eq 'bash'){print <<'END'}else {warn "No such shell completion: $type\n"}}sub completions {my$self=shift;my$cword=shift;my@words=@_;my$current=$words[$cword]|| '';my$prev=$words[$cword - 1]|| '';my$reply;if ($prev eq '--format' || $prev eq '-f'){$reply=$self->_completion_formats}elsif ($current =~ /^-/){$reply=$self->_completion_options}else {if (!$self->command){$reply=[$self->commands,@{$self->_completion_options([keys %{$self->early_options}])}]}else {print 'file';exit 9}}local $,="\n";print grep {/^\Q$current\E/}@$reply;exit 0}sub _completion_options {my$self=shift;my$opts=shift || [$self->options];my@options;for my$option (@$opts){my ($names,$op,$vtype)=$option =~ /^([^=:!]+)([=:!]?)(.*)$/;my@names=split(/\|/,$names);for my$name (@names){if ($op eq '!'){push@options,"--$name","--no-$name"}else {if (length($name)> 1){push@options,"--$name"}else {push@options,"-$name"}}}}return [sort@options]}sub _completion_formats {[qw(csv json json:pretty tsv yaml)]}1;
   # git-codeowners - Bash completion
   # To use, eval this code:
   #   eval "$(git-codeowners --shell-completion)"
@@ -56,14 +84,14 @@ $fatpacked{"App/Codeowners/Options.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\
 APP_CODEOWNERS_OPTIONS
 
 $fatpacked{"App/Codeowners/Util.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'APP_CODEOWNERS_UTIL';
-  package App::Codeowners::Util;use warnings;use strict;use Encode qw(decode);use Exporter qw(import);use Path::Tiny;our@EXPORT_OK=qw(colorstrip find_codeowners_in_directory find_nearest_codeowners git_ls_files git_toplevel run_git stringf unbackslash);our$VERSION='0.41';sub find_nearest_codeowners {my$path=path(shift || '.')->absolute;while (!$path->is_rootdir){my$filepath=find_codeowners_in_directory($path);return$filepath if$filepath;$path=$path->parent}}sub find_codeowners_in_directory {my$path=path(shift)or die;my@tries=([qw(CODEOWNERS)],[qw(docs CODEOWNERS)],[qw(.bitbucket CODEOWNERS)],[qw(.github CODEOWNERS)],[qw(.gitlab CODEOWNERS)],);for my$parts (@tries){my$try=$path->child(@$parts);return$try if$try->is_file}}sub run_git {my@cmd=('git',@_);require IPC::Open2;my ($child_in,$child_out);my$pid=IPC::Open2::open2($child_out,$child_in,@cmd);close($child_in);binmode($child_out,':encoding(UTF-8)');chomp(my@lines=<$child_out>);waitpid($pid,0);return if $?!=0;return@lines}sub git_ls_files {my$dir=shift || '.';my@files=run_git('-C',$dir,qw{ls-files},@_);return undef if!@files;for my$file (@files){next if$file !~ /^"(.+)"$/;$file=$1;$file=unbackslash($file);$file=decode('UTF-8',$file)}return \@files}sub git_toplevel {my$dir=shift || '.';my ($path)=run_git('-C',$dir,qw{rev-parse --show-toplevel});return if!$path;return path($path)}sub colorstrip {my$str=shift || '';$str =~ s/\e\[[\d;]*m//g;return$str}sub _replace {my ($args,$orig,$alignment,$min_width,$max_width,$passme,$formchar)=@_;return$orig unless defined$args->{$formchar};$alignment='+' unless defined$alignment;my$replacement=$args->{$formchar};if (ref$replacement eq 'CODE'){$passme ||= "";$passme =~ tr/{}//d;$replacement=$replacement->($passme)}my$replength;if (eval {require Unicode::GCString}){my$gcstring=Unicode::GCString->new(colorstrip($replacement));$replength=$gcstring->columns}else {$replength=length colorstrip($replacement)}$min_width ||= $replength;$max_width ||= $replength;if (($replength > $min_width)&& ($replength < $max_width)){return$replacement}if ($replength > $max_width){return substr($replacement,0,$max_width)}my$padding=$min_width - $replength;$padding=0 if$padding < 0;if ($alignment eq '-'){return$replacement .' ' x $padding}return ' ' x $padding .$replacement}my$regex=qr/
+  package App::Codeowners::Util;use warnings;use strict;use Encode qw(decode);use Exporter qw(import);use Path::Tiny;our@EXPORT_OK=qw(colorstrip find_codeowners_in_directory find_nearest_codeowners git_ls_files git_toplevel run_command run_git stringf stringify unbackslash zip);our$VERSION='0.42';sub find_nearest_codeowners {my$path=path(shift || '.')->absolute;while (!$path->is_rootdir){my$filepath=find_codeowners_in_directory($path);return$filepath if$filepath;$path=$path->parent}}sub find_codeowners_in_directory {my$path=path(shift)or die;my@tries=([qw(CODEOWNERS)],[qw(docs CODEOWNERS)],[qw(.bitbucket CODEOWNERS)],[qw(.github CODEOWNERS)],[qw(.gitlab CODEOWNERS)],);for my$parts (@tries){my$try=$path->child(@$parts);return$try if$try->is_file}}sub run_command {my$filter;$filter=pop if ref($_[-1])eq 'CODE';print STDERR "# @_\n" if$ENV{GIT_CODEOWNERS_DEBUG};my ($child_in,$child_out);require IPC::Open2;my$pid=IPC::Open2::open2($child_out,$child_in,@_);close($child_in);binmode($child_out,':encoding(UTF-8)');my$proc=App::Codeowners::Util::Process->new(pid=>$pid,fh=>$child_out,filter=>$filter,);return wantarray ? ($proc,@{$proc->all}): $proc}sub run_git {return run_command('git',@_)}sub git_ls_files {my$dir=shift || '.';return run_git('-C',$dir,'ls-files',@_,\&_unescape_git_filepath)}sub _unescape_git_filepath {return $_ if $_ !~ /^"(.+)"$/;return decode('UTF-8',unbackslash($1))}sub git_toplevel {my$dir=shift || '.';my ($proc,$path)=run_git('-C',$dir,qw{rev-parse --show-toplevel});return if$proc->wait!=0 ||!$path;return path($path)}sub colorstrip {my$str=shift || '';$str =~ s/\e\[[\d;]*m//g;return$str}sub stringify {my$item=shift;return ref($item)eq 'ARRAY' ? join(',',@$item): $item}sub zip (\@\@) {my$max=-1;$max < $#$_ && ($max=$#$_)foreach @_;map {my$ix=$_;map $_->[$ix],@_}0 .. $max}sub _replace {my ($args,$orig,$alignment,$min_width,$max_width,$passme,$formchar)=@_;return$orig unless defined$args->{$formchar};$alignment='+' unless defined$alignment;my$replacement=$args->{$formchar};if (ref$replacement eq 'CODE'){$passme ||= "";$passme =~ tr/{}//d;$replacement=$replacement->($passme)}my$replength;if (eval {require Unicode::GCString}){my$gcstring=Unicode::GCString->new(colorstrip($replacement));$replength=$gcstring->columns}else {$replength=length colorstrip($replacement)}$min_width ||= $replength;$max_width ||= $replength;if (($replength > $min_width)&& ($replength < $max_width)){return$replacement}if ($replength > $max_width){return substr($replacement,0,$max_width)}my$padding=$min_width - $replength;$padding=0 if$padding < 0;if ($alignment eq '-'){return$replacement .' ' x $padding}return ' ' x $padding .$replacement}my$regex=qr/
                  (%             # leading '%'
                   (-)?          # left-align, rather than right
                   (\d*)?        # (optional) minimum field width
                   (?:\.(\d*))?  # (optional) maximum field width
                   (\{.*?\})?    # (optional) stuff inside
                   (\S)          # actual format character
-               )/x;sub stringf {my$format=shift || return;my$args=UNIVERSAL::isa($_[0],'HASH')? shift : {@_};$args->{'n'}="\n" unless exists$args->{'n'};$args->{'t'}="\t" unless exists$args->{'t'};$args->{'%'}="%" unless exists$args->{'%'};$format =~ s/$regex/_replace($args, $1, $2, $3, $4, $5, $6)/ge;return$format}my%unbackslash;sub unbackslash {my$str=shift;%unbackslash=((map {$_=>$_}('\\','"','$','@')),('r'=>"\r",'n'=>"\n",'t'=>"\t"),(map {'x' .unpack('H2',chr($_))=>chr($_)}(0..255)),(map {sprintf('%03o',$_)=>chr($_)}(0..255)),('a'=>"\x07",'b'=>"\x08",'f'=>"\x0c",'v'=>"\x0b"),)if!%unbackslash;$str =~ s/ (\A|\G|[^\\]) \\ ( [0-7]{3} | x[\da-fA-F]{2} | . ) / $1 . $unbackslash{lc($2)} /gsxe;return$str}1;
+               )/x;sub stringf {my$format=shift || return;my$args=UNIVERSAL::isa($_[0],'HASH')? shift : {@_};$args->{'n'}="\n" unless exists$args->{'n'};$args->{'t'}="\t" unless exists$args->{'t'};$args->{'%'}="%" unless exists$args->{'%'};$format =~ s/$regex/_replace($args, $1, $2, $3, $4, $5, $6)/ge;return$format}my%unbackslash;sub unbackslash {my$str=shift;%unbackslash=((map {$_=>$_}('\\','"','$','@')),('r'=>"\r",'n'=>"\n",'t'=>"\t"),(map {'x' .unpack('H2',chr($_))=>chr($_)}(0..255)),(map {sprintf('%03o',$_)=>chr($_)}(0..255)),('a'=>"\x07",'b'=>"\x08",'f'=>"\x0c",'v'=>"\x0b"),)if!%unbackslash;$str =~ s/ (\A|\G|[^\\]) \\ ( [0-7]{3} | x[\da-fA-F]{2} | . ) / $1 . $unbackslash{lc($2)} /gsxe;return$str}{package App::Codeowners::Util::Process;sub new {my$class=shift;return bless {@_},$class}sub next {my$self=shift;my$line=readline($self->{fh});if (defined$line){chomp$line;if (my$filter=$self->{filter}){local $_=$line;$line=$filter->($line)}}$line}sub all {my$self=shift;chomp(my@lines=readline($self->{fh}));if (my$filter=$self->{filter}){$_=$filter->($_)for@lines}\@lines}sub wait {my$self=shift;my$pid=$self->{pid}or return;if (my$fh=$self->{fh}){close($fh);delete$self->{fh}}waitpid($pid,0);my$status=$?;print STDERR "# -> status $status\n" if$ENV{GIT_CODEOWNERS_DEBUG};delete$self->{pid};return$status}sub DESTROY {my ($self,$global_destruction)=@_;return if$global_destruction;$self->wait}}1;
 APP_CODEOWNERS_UTIL
 
 $fatpacked{"Color/ANSI/Util.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'COLOR_ANSI_UTIL';
@@ -97,7 +125,7 @@ $fatpacked{"Color/RGB/Util.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'
 COLOR_RGB_UTIL
 
 $fatpacked{"File/Codeowners.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'FILE_CODEOWNERS';
-  package File::Codeowners;use v5.10.1;use warnings;use strict;use Encode qw(encode);use Path::Tiny;use Scalar::Util qw(openhandle);use Text::Gitignore qw(build_gitignore_matcher);our$VERSION='0.41';sub _croak {require Carp;Carp::croak(@_)}sub _usage {_croak("Usage: @_\n")}sub new {my$class=shift;my$self=bless {},$class}sub parse {my$self=shift;my$input=shift or _usage(q{$codeowners->parse($input)});return$self->parse_from_array($input,@_)if @_;return$self->parse_from_array($input)if ref($input)eq 'ARRAY';return$self->parse_from_string($input)if ref($input)eq 'SCALAR';return$self->parse_from_fh($input)if openhandle($input);return$self->parse_from_filepath($input)}sub parse_from_filepath {my$self=shift;my$path=shift or _usage(q{$codeowners->parse_from_filepath($filepath)});$self=bless({},$self)if!ref($self);return$self->parse_from_fh(path($path)->openr_utf8)}sub parse_from_fh {my$self=shift;my$fh=shift or _usage(q{$codeowners->parse_from_fh($fh)});$self=bless({},$self)if!ref($self);my@lines;my$parse_unowned;my%unowned;my$current_project;while (my$line=<$fh>){my$lineno=$. - 1;chomp$line;if ($line eq '### UNOWNED (File::Codeowners)'){$parse_unowned++;last}elsif ($line =~ /^\h*#(.*)/){my$comment=$1;if ($comment =~ /^\h*Project:\h*(.+?)\h*$/i){$current_project=$1 || undef}$lines[$lineno]={comment=>$comment,}}elsif ($line =~ /^\h*$/){}elsif ($line =~ /^\h*(.+?)(?<!\\)\h+(.+)/){my$pattern=$1;my@owners=$2 =~ /( (?:\@+"[^"]*") | (?:\H+) )/gx;$lines[$lineno]={pattern=>$pattern,owners=>\@owners,$current_project ? (project=>$current_project): (),}}else {die "Parse error on line $.: $line\n"}}if ($parse_unowned){while (my$line=<$fh>){chomp$line;if ($line =~ /# (.+)/){my$filepath=$1;$unowned{$filepath}++}}}$self->{lines}=\@lines;$self->{unowned}=\%unowned;return$self}sub parse_from_array {my$self=shift;my$arr=shift or _usage(q{$codeowners->parse_from_array(\@lines)});$self=bless({},$self)if!ref($self);$arr=[$arr,@_]if @_;my$str=join("\n",@$arr);return$self->parse_from_string(\$str)}sub parse_from_string {my$self=shift;my$str=shift or _usage(q{$codeowners->parse_from_string(\$string)});$self=bless({},$self)if!ref($self);my$ref=ref($str)eq 'SCALAR' ? $str : \$str;open(my$fh,'<:encoding(UTF-8)',$ref)or die "open failed: $!";return$self->parse_from_fh($fh)}sub write_to_filepath {my$self=shift;my$path=shift or _usage(q{$codeowners->write_to_filepath($filepath)});path($path)->spew_utf8([map {"$_\n"}@{$self->write_to_array('')}])}sub write_to_fh {my$self=shift;my$fh=shift or _usage(q{$codeowners->write_to_fh($fh)});for my$line (@{$self->write_to_array}){print$fh "$line\n"}}sub write_to_string {my$self=shift;my$str=join("\n",@{$self->write_to_array})."\n";return \$str}sub write_to_array {my$self=shift;my$charset=shift // 'UTF-8';my@format;for my$line (@{$self->_lines}){if (my$comment=$line->{comment}){push@format,"#$comment"}elsif (my$pattern=$line->{pattern}){my$owners=join(' ',@{$line->{owners}});push@format,"$pattern  $owners"}else {push@format,''}}my@unowned=sort keys %{$self->_unowned};if (@unowned){push@format,'' if$format[-1];push@format,'### UNOWNED (File::Codeowners)';for my$unowned (@unowned){push@format,"# $unowned"}}if ($charset){$_=encode($charset,$_)for@format}return \@format}sub match {my$self=shift;my$filepath=shift or _usage(q{$codeowners->match($filepath)});my$lines=$self->{match_lines}||= [reverse grep {($_ || {})->{pattern}}@{$self->_lines}];for my$line (@$lines){my$matcher=$line->{matcher}||= build_gitignore_matcher([$line->{pattern}]);return {pattern=>$line->{pattern},owners=>[@{$line->{owners}|| []}],$line->{project}? (project=>$line->{project}): (),}if$matcher->($filepath)}return undef}sub owners {my$self=shift;my$pattern=shift;return$self->{owners}if!$pattern && $self->{owners};my%owners;for my$line (@{$self->_lines}){next if$pattern && $line->{pattern}&& $pattern ne $line->{pattern};$owners{$_}++ for (@{$line->{owners}|| []})}my$owners=[sort keys%owners];$self->{owners}=$owners if!$pattern;return$owners}sub patterns {my$self=shift;my$owner=shift;return$self->{patterns}if!$owner && $self->{patterns};my%patterns;for my$line (@{$self->_lines}){next if$owner &&!grep {$_ eq $owner}@{$line->{owners}|| []};my$pattern=$line->{pattern};$patterns{$pattern}++ if$pattern}my$patterns=[sort keys%patterns];$self->{patterns}=$patterns if!$owner;return$patterns}sub update_owners {my$self=shift;my$pattern=shift;my$owners=shift;$pattern && $owners or _usage(q{$codeowners->update_owners($pattern => \@owners)});$owners=[$owners]if ref($owners)ne 'ARRAY';$self->_clear;for my$line (@{$self->_lines}){next if!$line->{pattern};next if$pattern ne $line->{pattern};$line->{owners}=[@$owners]}}sub append {my$self=shift;$self->_clear;push @{$self->_lines},(@_ ? {@_}: undef)}sub prepend {my$self=shift;$self->_clear;unshift @{$self->_lines},(@_ ? {@_}: undef)}sub unowned {my$self=shift;[sort keys %{$self->{unowned}|| {}}]}sub add_unowned {my$self=shift;$self->_unowned->{$_}++ for @_}sub remove_unowned {my$self=shift;delete$self->_unowned->{$_}for @_}sub is_unowned {my$self=shift;my$filepath=shift;$self->_unowned->{$filepath}}sub clear_unowned {my$self=shift;$self->{unowned}={}}sub _lines {shift->{lines}||= []}sub _unowned {shift->{unowned}||= {}}sub _clear {my$self=shift;delete$self->{match_lines};delete$self->{owners};delete$self->{patterns}}1;
+  package File::Codeowners;use v5.10.1;use warnings;use strict;use Encode qw(encode);use Path::Tiny;use Scalar::Util qw(openhandle);use Text::Gitignore qw(build_gitignore_matcher);our$VERSION='0.42';sub _croak {require Carp;Carp::croak(@_)}sub _usage {_croak("Usage: @_\n")}sub new {my$class=shift;my$self=bless {},$class}sub parse {my$self=shift;my$input=shift or _usage(q{$codeowners->parse($input)});return$self->parse_from_array($input,@_)if @_;return$self->parse_from_array($input)if ref($input)eq 'ARRAY';return$self->parse_from_string($input)if ref($input)eq 'SCALAR';return$self->parse_from_fh($input)if openhandle($input);return$self->parse_from_filepath($input)}sub parse_from_filepath {my$self=shift;my$path=shift or _usage(q{$codeowners->parse_from_filepath($filepath)});$self=bless({},$self)if!ref($self);return$self->parse_from_fh(path($path)->openr_utf8)}sub parse_from_fh {my$self=shift;my$fh=shift or _usage(q{$codeowners->parse_from_fh($fh)});$self=bless({},$self)if!ref($self);my@lines;my$parse_unowned;my%unowned;my$current_project;while (my$line=<$fh>){my$lineno=$. - 1;chomp$line;if ($line eq '### UNOWNED (File::Codeowners)'){$parse_unowned++;last}elsif ($line =~ /^\h*#(.*)/){my$comment=$1;if ($comment =~ /^\h*Project:\h*(.+?)\h*$/i){$current_project=$1 || undef}$lines[$lineno]={comment=>$comment,}}elsif ($line =~ /^\h*$/){}elsif ($line =~ /^\h*(.+?)(?<!\\)\h+(.+)/){my$pattern=$1;my@owners=$2 =~ /( (?:\@+"[^"]*") | (?:\H+) )/gx;$lines[$lineno]={pattern=>$pattern,owners=>\@owners,$current_project ? (project=>$current_project): (),}}else {die "Parse error on line $.: $line\n"}}if ($parse_unowned){while (my$line=<$fh>){chomp$line;if ($line =~ /# (.+)/){my$filepath=$1;$unowned{$filepath}++}}}$self->{lines}=\@lines;$self->{unowned}=\%unowned;return$self}sub parse_from_array {my$self=shift;my$arr=shift or _usage(q{$codeowners->parse_from_array(\@lines)});$self=bless({},$self)if!ref($self);$arr=[$arr,@_]if @_;my$str=join("\n",@$arr);return$self->parse_from_string(\$str)}sub parse_from_string {my$self=shift;my$str=shift or _usage(q{$codeowners->parse_from_string(\$string)});$self=bless({},$self)if!ref($self);my$ref=ref($str)eq 'SCALAR' ? $str : \$str;open(my$fh,'<:encoding(UTF-8)',$ref)or die "open failed: $!";return$self->parse_from_fh($fh)}sub write_to_filepath {my$self=shift;my$path=shift or _usage(q{$codeowners->write_to_filepath($filepath)});path($path)->spew_utf8([map {"$_\n"}@{$self->write_to_array('')}])}sub write_to_fh {my$self=shift;my$fh=shift or _usage(q{$codeowners->write_to_fh($fh)});for my$line (@{$self->write_to_array}){print$fh "$line\n"}}sub write_to_string {my$self=shift;my$str=join("\n",@{$self->write_to_array})."\n";return \$str}sub write_to_array {my$self=shift;my$charset=shift // 'UTF-8';my@format;for my$line (@{$self->_lines}){if (my$comment=$line->{comment}){push@format,"#$comment"}elsif (my$pattern=$line->{pattern}){my$owners=join(' ',@{$line->{owners}});push@format,"$pattern  $owners"}else {push@format,''}}my@unowned=sort keys %{$self->_unowned};if (@unowned){push@format,'' if$format[-1];push@format,'### UNOWNED (File::Codeowners)';for my$unowned (@unowned){push@format,"# $unowned"}}if ($charset){$_=encode($charset,$_)for@format}return \@format}sub match {my$self=shift;my$filepath=shift or _usage(q{$codeowners->match($filepath)});my$lines=$self->{match_lines}||= [reverse grep {($_ || {})->{pattern}}@{$self->_lines}];for my$line (@$lines){my$matcher=$line->{matcher}||= build_gitignore_matcher([$line->{pattern}]);return {pattern=>$line->{pattern},owners=>[@{$line->{owners}|| []}],$line->{project}? (project=>$line->{project}): (),}if$matcher->($filepath)}return undef}sub owners {my$self=shift;my$pattern=shift;return$self->{owners}if!$pattern && $self->{owners};my%owners;for my$line (@{$self->_lines}){next if$pattern && $line->{pattern}&& $pattern ne $line->{pattern};$owners{$_}++ for (@{$line->{owners}|| []})}my$owners=[sort keys%owners];$self->{owners}=$owners if!$pattern;return$owners}sub patterns {my$self=shift;my$owner=shift;return$self->{patterns}if!$owner && $self->{patterns};my%patterns;for my$line (@{$self->_lines}){next if$owner &&!grep {$_ eq $owner}@{$line->{owners}|| []};my$pattern=$line->{pattern};$patterns{$pattern}++ if$pattern}my$patterns=[sort keys%patterns];$self->{patterns}=$patterns if!$owner;return$patterns}sub projects {my$self=shift;return$self->{projects}if$self->{projects};my%projects;for my$line (@{$self->_lines}){my$project=$line->{project};$projects{$project}++ if$project}my$projects=[sort keys%projects];$self->{projects}=$projects;return$projects}sub update_owners {my$self=shift;my$pattern=shift;my$owners=shift;$pattern && $owners or _usage(q{$codeowners->update_owners($pattern => \@owners)});$owners=[$owners]if ref($owners)ne 'ARRAY';$self->_clear;for my$line (@{$self->_lines}){next if!$line->{pattern};next if$pattern ne $line->{pattern};$line->{owners}=[@$owners]}}sub append {my$self=shift;$self->_clear;push @{$self->_lines},(@_ ? {@_}: undef)}sub prepend {my$self=shift;$self->_clear;unshift @{$self->_lines},(@_ ? {@_}: undef)}sub unowned {my$self=shift;[sort keys %{$self->{unowned}|| {}}]}sub add_unowned {my$self=shift;$self->_unowned->{$_}++ for @_}sub remove_unowned {my$self=shift;delete$self->_unowned->{$_}for @_}sub is_unowned {my$self=shift;my$filepath=shift;$self->_unowned->{$filepath}}sub clear_unowned {my$self=shift;$self->{unowned}={}}sub _lines {shift->{lines}||= []}sub _unowned {shift->{unowned}||= {}}sub _clear {my$self=shift;delete$self->{match_lines};delete$self->{owners};delete$self->{patterns};delete$self->{projects}}1;
 FILE_CODEOWNERS
 
 $fatpacked{"File/Which.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'FILE_WHICH';
@@ -190,33 +218,10 @@ $fatpacked{"Proc/Find/Parents.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".
                       (?: -[+-]- )?/gx){unless ($1){my$p={name=>$2,pid=>$3};$p[$i]=$p;$p->{ppid}=$p[$i-1]{pid}if$i > 0;$proc{$3}=$p}$i++}}}else {eval {require Proc::ProcessTable};return undef if $@;state$pt=Proc::ProcessTable->new;for my$p (@{$pt->table}){$proc{$p->{pid}}={name=>$p->{fname},cmdline=>$p->{cmndline},pid=>$p->{pid},ppid=>$p->{ppid},uid=>$p->{uid},gid=>$p->{gid},pgrp=>$p->{pgrp},sess=>$p->{sess},sgid=>$p->{sgid},euid=>$p->{euid},egid=>$p->{egid},ttydev=>$p->{ttydev},ttynum=>$p->{ttynum},}}}my@p=();my$cur_pid=$pid;while (1){return if!$proc{$cur_pid};$proc{$cur_pid}{name}=$1 if$proc{$cur_pid}{name}=~ /\A\{(.+)\}\z/;push@p,$proc{$cur_pid};$cur_pid=$proc{$cur_pid}{ppid};last unless$cur_pid}shift@p;\@p}1;
 PROC_FIND_PARENTS
 
-$fatpacked{"Term/ANSIColor.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'TERM_ANSICOLOR';
-  package Term::ANSIColor;use 5.006;use strict;use warnings;use Exporter ();our (@EXPORT,@EXPORT_OK,%EXPORT_TAGS,@ISA,$VERSION);our$AUTOLOAD;BEGIN {$VERSION='4.06';my@colorlist=qw(CLEAR RESET BOLD DARK FAINT ITALIC UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE ON_BLACK ON_RED ON_GREEN ON_YELLOW ON_BLUE ON_MAGENTA ON_CYAN ON_WHITE BRIGHT_BLACK BRIGHT_RED BRIGHT_GREEN BRIGHT_YELLOW BRIGHT_BLUE BRIGHT_MAGENTA BRIGHT_CYAN BRIGHT_WHITE ON_BRIGHT_BLACK ON_BRIGHT_RED ON_BRIGHT_GREEN ON_BRIGHT_YELLOW ON_BRIGHT_BLUE ON_BRIGHT_MAGENTA ON_BRIGHT_CYAN ON_BRIGHT_WHITE);my@colorlist256=((map {("ANSI$_","ON_ANSI$_")}0 .. 255),(map {("GREY$_","ON_GREY$_")}0 .. 23),);for my$r (0 .. 5){for my$g (0 .. 5){push(@colorlist256,map {("RGB$r$g$_","ON_RGB$r$g$_")}0 .. 5)}}@ISA=qw(Exporter);@EXPORT=qw(color colored);@EXPORT_OK=qw(uncolor colorstrip colorvalid coloralias);%EXPORT_TAGS=(constants=>\@colorlist,constants256=>\@colorlist256,pushpop=>[@colorlist,qw(PUSHCOLOR POPCOLOR LOCALCOLOR)],);Exporter::export_ok_tags('pushpop','constants256')}our$AUTOLOCAL;our$AUTORESET;our$EACHLINE;our%ATTRIBUTES=('clear'=>0,'reset'=>0,'bold'=>1,'dark'=>2,'faint'=>2,'italic'=>3,'underline'=>4,'underscore'=>4,'blink'=>5,'reverse'=>7,'concealed'=>8,'black'=>30,'on_black'=>40,'red'=>31,'on_red'=>41,'green'=>32,'on_green'=>42,'yellow'=>33,'on_yellow'=>43,'blue'=>34,'on_blue'=>44,'magenta'=>35,'on_magenta'=>45,'cyan'=>36,'on_cyan'=>46,'white'=>37,'on_white'=>47,'bright_black'=>90,'on_bright_black'=>100,'bright_red'=>91,'on_bright_red'=>101,'bright_green'=>92,'on_bright_green'=>102,'bright_yellow'=>93,'on_bright_yellow'=>103,'bright_blue'=>94,'on_bright_blue'=>104,'bright_magenta'=>95,'on_bright_magenta'=>105,'bright_cyan'=>96,'on_bright_cyan'=>106,'bright_white'=>97,'on_bright_white'=>107,);for my$code (0 .. 15){$ATTRIBUTES{"ansi$code"}="38;5;$code";$ATTRIBUTES{"on_ansi$code"}="48;5;$code"}for my$r (0 .. 5){for my$g (0 .. 5){for my$b (0 .. 5){my$code=16 + (6 * 6 * $r)+ (6 * $g)+ $b;$ATTRIBUTES{"rgb$r$g$b"}="38;5;$code";$ATTRIBUTES{"on_rgb$r$g$b"}="48;5;$code"}}}for my$n (0 .. 23){my$code=$n + 232;$ATTRIBUTES{"grey$n"}="38;5;$code";$ATTRIBUTES{"on_grey$n"}="48;5;$code"}our%ATTRIBUTES_R;for my$attr (reverse sort keys%ATTRIBUTES){$ATTRIBUTES_R{$ATTRIBUTES{$attr}}=$attr}for my$code (16 .. 255){$ATTRIBUTES{"ansi$code"}="38;5;$code";$ATTRIBUTES{"on_ansi$code"}="48;5;$code"}our%ALIASES;if (exists$ENV{ANSI_COLORS_ALIASES}){my$spec=$ENV{ANSI_COLORS_ALIASES};$spec =~ s{\s+}{}xmsg;for my$definition (split m{,}xms,$spec){my ($new,$old)=split m{=}xms,$definition,2;if (!$new ||!$old){warn qq{Bad color mapping "$definition"}}else {my$result=eval {coloralias($new,$old)};if (!$result){my$error=$@;$error =~ s{ [ ] at [ ] .* }{}xms;warn qq{$error in "$definition"}}}}}our@COLORSTACK;sub croak {my (@args)=@_;require Carp;Carp::croak(@args)}sub AUTOLOAD {my ($sub,$attr)=$AUTOLOAD =~ m{
-          \A ( [a-zA-Z0-9:]* :: ([A-Z0-9_]+) ) \z
-      }xms;if (!($attr && defined($ATTRIBUTES{lc$attr }))){croak("undefined subroutine &$AUTOLOAD called")}if ($ENV{ANSI_COLORS_DISABLED}){return join(q{},@_)}$AUTOLOAD=$sub;my$escape="\e[" .$ATTRIBUTES{lc$attr }.'m';my$eval_err=$@;my$eval_result=eval qq{
-          sub $AUTOLOAD {
-              if (\$ENV{ANSI_COLORS_DISABLED}) {
-                  return join(q{}, \@_);
-              } elsif (\$AUTOLOCAL && \@_) {
-                  return PUSHCOLOR('$escape') . join(q{}, \@_) . POPCOLOR;
-              } elsif (\$AUTORESET && \@_) {
-                  return '$escape' . join(q{}, \@_) . "\e[0m";
-              } else {
-                  return '$escape' . join(q{}, \@_);
-              }
-          }
-          1;
-      };if (!$eval_result){die "failed to generate constant $attr: $@"}$@=$eval_err;goto &$AUTOLOAD}sub PUSHCOLOR {my (@text)=@_;my$text=join(q{},@text);my ($color)=$text =~ m{ \A ( (?:\e\[ [\d;]+ m)+ ) }xms;if (@COLORSTACK){$color=$COLORSTACK[-1].$color}push(@COLORSTACK,$color);return$text}sub POPCOLOR {my (@text)=@_;pop(@COLORSTACK);if (@COLORSTACK){return$COLORSTACK[-1].join(q{},@text)}else {return RESET(@text)}}sub LOCALCOLOR {my (@text)=@_;return PUSHCOLOR(join(q{},@text)).POPCOLOR()}sub color {my (@codes)=@_;@codes=map {split}@codes;if ($ENV{ANSI_COLORS_DISABLED}){return q{}}my$attribute=q{};for my$code (@codes){$code=lc($code);if (defined($ATTRIBUTES{$code})){$attribute .= $ATTRIBUTES{$code}.q{;}}elsif (defined($ALIASES{$code})){$attribute .= $ALIASES{$code}.q{;}}else {croak("Invalid attribute name $code")}}chop($attribute);return ($attribute ne q{})? "\e[${attribute}m" : undef}sub uncolor {my (@escapes)=@_;my (@nums,@result);for my$escape (@escapes){$escape =~ s{ \A \e\[ }{}xms;$escape =~ s{ m \z }   {}xms;my ($attrs)=$escape =~ m{ \A ((?:\d+;)* \d*) \z }xms;if (!defined($attrs)){croak("Bad escape sequence $escape")}push(@nums,$attrs =~ m{ ( 0*[34]8;0*5;\d+ | \d+ ) (?: ; | \z ) }xmsg)}for my$num (@nums){$num =~ s{ ( \A | ; ) 0+ (\d) }{$1$2}xmsg;my$name=$ATTRIBUTES_R{$num};if (!defined($name)){croak("No name for escape sequence $num")}push(@result,$name)}return@result}sub colored {my ($first,@rest)=@_;my ($string,@codes);if (ref($first)&& ref($first)eq 'ARRAY'){@codes=@{$first};$string=join(q{},@rest)}else {$string=$first;@codes=@rest}if ($ENV{ANSI_COLORS_DISABLED}){return$string}my$attr=color(@codes);if (defined($EACHLINE)){my@text=map {($_ ne $EACHLINE)? $attr .$_ ."\e[0m" : $_}grep {length > 0}split(m{ (\Q$EACHLINE\E) }xms,$string);return join(q{},@text)}else {return$attr .$string ."\e[0m"}}sub coloralias {my ($alias,$color)=@_;if (!defined($color)){if (!exists$ALIASES{$alias}){return}else {return$ATTRIBUTES_R{$ALIASES{$alias}}}}if ($alias !~ m{ \A [a-zA-Z0-9._-]+ \z }xms){croak(qq{Invalid alias name "$alias"})}elsif ($ATTRIBUTES{$alias}){croak(qq{Cannot alias standard color "$alias"})}elsif (!exists$ATTRIBUTES{$color}){croak(qq{Invalid attribute name "$color"})}$ALIASES{$alias}=$ATTRIBUTES{$color};return$color}sub colorstrip {my (@string)=@_;for my$string (@string){$string =~ s{ \e\[ [\d;]* m }{}xmsg}return wantarray ? @string : join(q{},@string)}sub colorvalid {my (@codes)=@_;@codes=map {split(q{ },lc)}@codes;for my$code (@codes){if (!(defined($ATTRIBUTES{$code})|| defined($ALIASES{$code}))){return}}return 1}1;
-TERM_ANSICOLOR
-
 $fatpacked{"Term/Detect/Software.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'TERM_DETECT_SOFTWARE';
   package Term::Detect::Software;our$DATE='2019-08-21';our$VERSION='0.222';use 5.010001;use strict;use warnings;use experimental 'smartmatch';require Exporter;our@ISA=qw(Exporter);our@EXPORT_OK=qw(detect_terminal detect_terminal_cached);my$dt_cache;sub detect_terminal_cached {if (!$dt_cache){$dt_cache=detect_terminal(@_)}$dt_cache}sub detect_terminal {my@dbg;my$info={_debug_info=>\@dbg};DETECT: {unless (defined$ENV{TERM}){push@dbg,"skip: TERM env undefined";$info->{emulator_engine}='';$info->{emulator_software}='';last DETECT}if ($ENV{KONSOLE_DBUS_SERVICE}|| $ENV{KONSOLE_DBUS_SESSION}){push@dbg,"detect: konsole via KONSOLE_DBUS_{SERVICE,SESSION} env";$info->{emulator_engine}='konsole';$info->{color_depth}=2**24;$info->{default_bgcolor}='000000';$info->{unicode}=1;$info->{box_chars}=1;last DETECT}if ($ENV{XTERM_VERSION}){push@dbg,"detect: xterm via XTERM_VERSION env";$info->{emulator_engine}='xterm';$info->{color_depth}=256;$info->{default_bgcolor}='ffffff';$info->{unicode}=0;$info->{box_chars}=1;last DETECT}if ($ENV{TERM}eq 'xterm' && ($ENV{OSTYPE}// '')eq 'cygwin'){push@dbg,"detect: xterm via TERM env (cygwin)";$info->{emulator_engine}='cygwin';$info->{color_depth}=16;$info->{default_bgcolor}='000000';$info->{unicode}=0;$info->{box_chars}=1;last DETECT}if ($ENV{TERM}eq 'linux'){push@dbg,"detect: linux via TERM env";$info->{emulator_engine}='linux';$info->{color_depth}=16;$info->{default_bgcolor}='000000';$info->{unicode}=0;$info->{box_chars}=0;last DETECT}my$gnome_terminal_terms=[qw/gnome-terminal guake xfce4-terminal mlterm lxterminal/];my$set_gnome_terminal_term=sub {$info->{emulator_software}=$_[0];$info->{emulator_engine}='gnome-terminal';$info->{color_depth}=$_[0]=~ /xfce4/ ? 16 : 256;$info->{unicode}=1;if ($_[0]~~ [qw/mlterm/]){$info->{default_bgcolor}='ffffff'}else {$info->{default_bgcolor}='000000'}$info->{box_chars}=1};if (($ENV{COLORTERM}// '')~~ $gnome_terminal_terms){push@dbg,"detect: gnome-terminal via COLORTERM";$set_gnome_terminal_term->($ENV{COLORTERM});last DETECT}if ($ENV{TERM}eq 'dumb' && $ENV{windir}){push@dbg,"detect: windows via TERM & windir env";$info->{emulator_software}='windows';$info->{emulator_engine}='windows';$info->{color_depth}=16;$info->{unicode}=0;$info->{default_bgcolor}='000000';$info->{box_chars}=0;last DETECT}if ($ENV{TERM}eq 'dumb'){push@dbg,"detect: dumb via TERM env";$info->{emulator_software}='dumb';$info->{emulator_engine}='dumb';$info->{color_depth}=0;$info->{default_bgcolor}='000000';$info->{box_chars}=0;last DETECT}{last if $^O =~ /Win/;require Proc::Find::Parents;my$ppids=Proc::Find::Parents::get_parent_processes();unless (defined$ppids){push@dbg,"skip: get_parent_processes returns undef";last}my$proc=@$ppids >= 1 ? $ppids->[1]{name}: '';if ($proc ~~ $gnome_terminal_terms){push@dbg,"detect: gnome-terminal via procname ($proc)";$set_gnome_terminal_term->($proc);last DETECT}elsif ($proc ~~ [qw/rxvt mrxvt/]){push@dbg,"detect: rxvt via procname ($proc)";$info->{emulator_software}=$proc;$info->{emulator_engine}='rxvt';$info->{color_depth}=16;$info->{unicode}=0;$info->{default_bgcolor}='d6d2d0';$info->{box_chars}=1;last DETECT}elsif ($proc eq 'st' && $ENV{TERM}eq 'xterm-256color'){push@dbg,"detect: st via procname";$info->{emulator_software}='st';$info->{emulator_engine}='st';$info->{color_depth}=256;$info->{unicode}=1;$info->{default_bgcolor}='000000';$info->{box_chars}=1;last DETECT}elsif ($proc ~~ [qw/pterm/]){push@dbg,"detect: pterm via procname ($proc)";$info->{emulator_software}=$proc;$info->{emulator_engine}='putty';$info->{color_depth}=256;$info->{unicode}=0;$info->{default_bgcolor}='000000';last DETECT}elsif ($proc ~~ [qw/xvt/]){push@dbg,"detect: xvt via procname ($proc)";$info->{emulator_software}=$proc;$info->{emulator_engine}='xvt';$info->{color_depth}=0;$info->{unicode}=0;$info->{default_bgcolor}='d6d2d0';last DETECT}}{unless (exists$info->{color_depth}){if ($ENV{TERM}=~ /256color/){push@dbg,"detect color_depth: 256 via TERM env";$info->{color_depth}=256}else {require File::Which;if (File::Which::which("tput")){my$res=`tput colors` + 0;push@dbg,"detect color_depth: $res via tput";$res=16 if$res==8;$info->{color_depth}=$res}}}$info->{emulator_software}//= '(generic)';$info->{emulator_engine}//= '(generic)';$info->{unicode}//= 0;$info->{color_depth}//= 0;$info->{box_chars}//= 0;$info->{default_bgcolor}//= '000000'}}if ($ENV{INSIDE_EMACS}){$info->{inside_emacs}=1;$info->{box_chars}=0}$info}1;
 TERM_DETECT_SOFTWARE
 
-$fatpacked{"Text/Aligner.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'TEXT_ALIGNER';
-  package Text::Aligner;use strict;use warnings;use 5.008;BEGIN {use Exporter ();use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);$VERSION='0.13';@ISA=qw (Exporter);@EXPORT=qw ();@EXPORT_OK=qw ( align);%EXPORT_TAGS=()}sub align ($@) {my$ali=Text::Aligner->new(shift);$ali->_alloc(map ref eq 'SCALAR' ? $$_ : $_,@_);if (defined wantarray){my@just=map$ali->_justify(ref eq 'SCALAR' ? $$_ : $_),@_;return@just if wantarray;return join "\n",@just,''}else {for (@_){$_=$ali->_justify($_)for ref eq 'SCALAR' ? $$_ : $_}}}sub _new {my$class=shift;my ($width,$pos)=@_;bless {width=>$width,pos=>$pos,left=>Text::Aligner::MaxKeeper->new,right=>Text::Aligner::MaxKeeper->new,},$class}sub new {my ($class,$spec)=@_;$spec ||= 0;my$al;if (!ref($spec)and $spec =~ s/^auto/num/){$al=Text::Aligner::Auto->_new($spec)}else {$al=$class->_new(_compile_alispec($spec))}$al}sub _measure0 {my$al=shift;my$obj=shift;$obj='' unless defined$obj;my ($w,$p);if (ref$obj){($w,$p)=($obj->$al->{width}->(),$obj->$al->{pos}->())}else {($w,$p)=($al->{width}->($obj),$al->{pos}->($obj))}$_ ||= 0 for$w,$p;($p,$w - $p)}use Term::ANSIColor 2.02;sub _measure {my$al=shift;my$obj=shift;$obj='' unless defined$obj;my ($wmeth,$pmeth)=@{$al}{qw(width pos)};$obj=Term::ANSIColor::colorstrip($obj)unless ref$obj;my$w=ref$wmeth ? $wmeth->($obj): $obj->$wmeth;my$p=ref$pmeth ? $pmeth->($obj): $obj->$pmeth;$_ ||= 0 for$w,$p;($p,$w - $p)}sub _status {my@lr=($_[0]->{left}->max,$_[0]->{right}->max);return unless defined($lr[0])and defined($lr[1]);@lr}sub _alloc {my$al=shift;for (@_){my ($l,$r)=$al->_measure($_);$al->{left}->remember($l);$al->{right}->remember($r)}$al}sub _forget {my$al=shift;for (map defined()? $_ : '',@_){my ($l,$r)=$al->_measure($_);$al->{left}->forget($l);$al->{right}->forget($r)}$al}sub _spaces {my ($repeat_count)=@_;return (($repeat_count > 0)? (' ' x $repeat_count): '')}sub _justify {my$al=shift;my$str=shift;$str .= '';my ($l_pad,$r_pad)=$al->_padding($str);substr($str,0,-$l_pad)='' if$l_pad < 0;substr($str,$r_pad)='' if$r_pad < 0;return _spaces($l_pad).$str ._spaces($r_pad)}sub _padding {my$al=shift;my$str=shift;my ($this_l,$this_r)=$al->_measure($str);my ($l_pad,$r_pad)=(0,0);if ($al->_status){($l_pad,$r_pad)=$al->_status;$l_pad -= $this_l;$r_pad -= $this_r}($l_pad,$r_pad)}sub _compile_alispec {my$width=sub {length shift};my$pos;local $_=shift || '';if (ref()eq 'Regexp'){my$regex=$_;$pos=sub {local $_=shift;return m/$regex/ ? $-[0]: length}}else {s/^left/0/;s/^center/0.5/;s/^right/1/;if (_is_number($_)){my$proportion=$_;$pos=sub {int($proportion*length shift)}}elsif ($_ =~ /^(?:num|point)(?:\((.*))?/){my$point=defined $1 ? $1 : '';$point =~ s/\)$//;length$point or $point='.';$pos=sub {index(shift().$point,$point)}}else {$pos=sub {0}}}($width,$pos)}sub _is_number {my ($x)=@_;return 0 unless defined$x;return 0 if$x !~ /\d/;return 1 if$x =~ /^-?\d+\.?\d*$/;$x=Term::ANSIColor::colorstrip($x);$x =~ /^-?\d+\.?\d*$/}package Text::Aligner::Auto;sub _new {my$class=shift;my$numspec=shift;bless {num=>Text::Aligner->new('num'),other=>Text::Aligner->new,},$class}sub _alloc {my$aa=shift;my@num=grep _is_number($_),@_;my@other=grep!_is_number($_),@_;$aa->{num}->_alloc(@num);$aa->{other}->_alloc(@other);$aa}sub _forget {my$aa=shift;$aa->{num}->_forget(grep _is_number($_),@_);$aa->{other}->_forget(grep!_is_number($_),@_);$aa}sub _justify {my ($aa,$str)=@_;$str=$aa->{_is_number($str)? 'num' : 'other'}->_justify($str);my$combi=Text::Aligner->new;$combi->_alloc($aa->{num}->_justify(''))if$aa->{num}->_status;$combi->_alloc($aa->{other}->_justify(''))if$aa->{other}->_status;$combi->_justify($str)}BEGIN {*_is_number=\ &Text::Aligner::_is_number}package Text::Aligner::MaxKeeper;sub new {bless {max=>undef,seen=>{},},shift}sub max {$_[0]->{max}}sub remember {my ($mk,$val)=@_;_to_max($mk->{max},$val);$mk->{seen}->{$val}++;$mk}sub forget {my ($mk,$val)=@_;if (exists$mk->{seen}->{$val}){my$seen=$mk->{seen};unless (--$seen->{$val}){delete$seen->{$val};if ($mk->{max}==$val){undef$mk->{max};_to_max($mk->{max},keys %$seen)}}}$mk}sub _to_max {my$var=\ shift;defined $_ and (not defined $$var or $$var < $_)and $$var=$_ for @_;$$var}1;
-TEXT_ALIGNER
-
 $fatpacked{"Text/CSV.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'TEXT_CSV';
   package Text::CSV;use strict;use Exporter;use Carp ();use vars qw($VERSION $DEBUG @ISA @EXPORT_OK);@ISA=qw(Exporter);@EXPORT_OK=qw(csv);BEGIN {$VERSION='2.00';$DEBUG=0}my$Module_XS='Text::CSV_XS';my$Module_PP='Text::CSV_PP';my$XS_Version='1.02';my$Is_Dynamic=0;my@PublicMethods=qw/version error_diag error_input known_attributes csv PV IV NV/;unless ($Text::CSV::Worker){$Text::CSV::DEBUG and Carp::carp("Check used worker module...");if (exists$ENV{PERL_TEXT_CSV}){if ($ENV{PERL_TEXT_CSV}eq '0' or $ENV{PERL_TEXT_CSV}eq 'Text::CSV_PP'){_load_pp()or Carp::croak $@}elsif ($ENV{PERL_TEXT_CSV}eq '1' or $ENV{PERL_TEXT_CSV}=~ /Text::CSV_XS\s*,\s*Text::CSV_PP/){_load_xs()or _load_pp()or Carp::croak $@}elsif ($ENV{PERL_TEXT_CSV}eq '2' or $ENV{PERL_TEXT_CSV}eq 'Text::CSV_XS'){_load_xs()or Carp::croak $@}else {Carp::croak "The value of environmental variable 'PERL_TEXT_CSV' is invalid."}}else {_load_xs()or _load_pp()or Carp::croak $@}}sub new {my$proto=shift;my$class=ref($proto)|| $proto;unless ($proto){return eval qq| $Text::CSV::Worker\::new( \$proto ) |}if (my$obj=$Text::CSV::Worker->new(@_)){$obj->{_MODULE}=$Text::CSV::Worker;bless$obj,$class;return$obj}else {return}}sub require_xs_version {$XS_Version}sub module {my$proto=shift;return!ref($proto)? $Text::CSV::Worker : ref($proto->{_MODULE})? ref($proto->{_MODULE}): $proto->{_MODULE}}*backend=*module;sub is_xs {return $_[0]->module eq $Module_XS}sub is_pp {return $_[0]->module eq $Module_PP}sub is_dynamic {$Is_Dynamic}sub _load_xs {_load($Module_XS,$XS_Version)}sub _load_pp {_load($Module_PP)}sub _load {my ($module,$version)=@_;$version ||= '';$Text::CSV::DEBUG and Carp::carp "Load $module.";eval qq| use $module $version |;return if $@;push@Text::CSV::ISA,$module;$Text::CSV::Worker=$module;local $^W;no strict qw(refs);for my$method (@PublicMethods){*{"Text::CSV::$method"}=\&{"$module\::$method"}}return 1}1;
 TEXT_CSV
@@ -252,10 +257,6 @@ $fatpacked{"Text/Gitignore.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'
           }eg;$pattern .= '$' unless$pattern =~ m{\/$};return$pattern};my@patterns_re;for my$pattern (@$patterns){if ($pattern =~ m/^!/){my$re=$build_pattern->(substr$pattern,1);push@patterns_re,{re=>$re,negative=>1 }}else {$pattern =~ s{^\\!}{!};push@patterns_re,{re=>$build_pattern->($pattern)}}}my@negatives=grep {/^!/}@$patterns;return sub {my$path=shift;my$match=0;for my$pattern (@patterns_re){my$re=$pattern->{re};next if$match &&!$pattern->{negative};if ($pattern->{negative}){if ($path =~ m/$re/){$match=0}}else {$match=!!($path =~ m/$re/);if ($match &&!@negatives){return$match}}}return$match}}1;
 TEXT_GITIGNORE
 
-$fatpacked{"Text/Table.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'TEXT_TABLE';
-  package Text::Table;use strict;use warnings;use 5.008;use List::Util qw(sum max);use Text::Aligner qw(align);our$VERSION='1.133';use overload (bool=>sub {return 1},'""'=>'stringify',);sub _is_sep {my$datum=shift;return (defined($datum)and ((ref($datum)eq 'SCALAR')or (ref($datum)eq 'HASH' and $datum->{is_sep})))}sub _get_sep_title_body {my$sep=shift;return +(ref($sep)eq 'HASH')? @{$sep}{qw(title body)}: split(/\n/,${$sep},-1)}sub _parse_sep {my$sep=shift;if (!defined($sep)){$sep=''}my ($title,$body)=_get_sep_title_body($sep);if (!defined($body)){$body=$title}($title,$body)=align('left',$title,$body);return {is_sep=>1,title=>$title,body=>$body,}}sub _default_if_empty {my ($ref,$default)=@_;if (!(defined($$ref)&& length($$ref))){$$ref=$default}return}sub _is_align {my$align=shift;return$align =~ /\A(?:left|center|right)/}sub _parse_spec {my$spec=shift;if (!defined($spec)){$spec=''}my$alispec=qr/^ *(?:left|center|right|num|point|auto)/;my ($title,$align,$align_title,$align_title_lines,$sample);if (ref ($spec)eq 'HASH'){($title,$align,$align_title,$align_title_lines,$sample)=@{$spec}{qw(title align align_title align_title_lines sample)}}else {my$alispec=qr/&(.*)/;if ($spec =~ $alispec){($title,$align,$sample)=($spec =~ /(.*)^$alispec\n?(.*)/sm)}else {$title=$spec}for my$s ($title,$sample){if (defined($s)){chomp($s)}}}for my$x ($title,$sample){if (!defined($x)){$x=[]}elsif (ref($x)ne 'ARRAY'){$x=[split /\n/,$x,-1]}}_default_if_empty(\$align,'auto');unless (ref$align eq 'Regexp' or $align =~ /^(?:left|center|right|num\(?|point\(?|auto)/){_warn("Invalid align specification: '$align', using 'auto'");$align='auto'}_default_if_empty(\$align_title,'left');if (!_is_align($align_title)){_warn("Invalid align_title specification: " ."'$align_title', using 'left'",);$align_title='left'}_default_if_empty(\$align_title_lines,$align_title);if (!_is_align($align_title_lines)){_warn("Invalid align_title_lines specification: " ."'$align_title_lines', using 'left'",);$align_title_lines='left'}return {title=>$title,align=>$align,align_title=>$align_title,align_title_lines=>$align_title_lines,sample=>$sample,}}sub new {my$tb=bless {},shift;return$tb->_entitle([@_ ])}sub _blank {my$self=shift;if (@_){$self->{blank}=shift}return$self->{blank}}sub _cols {my$self=shift;if (@_){$self->{cols}=shift}return$self->{cols}}sub _forms {my$self=shift;if (@_){$self->{forms}=shift}return$self->{forms}}sub _lines {my$self=shift;if (@_){$self->{lines}=shift}return$self->{lines}}sub _spec {my$self=shift;if (@_){$self->{spec}=shift}return$self->{spec}}sub _titles {my$self=shift;if (@_){$self->{titles}=shift}return$self->{titles}}sub _entitle {my ($tb,$sep_list)=@_;my (@seps,@spec);my$sep;for my$sep_item (@{$sep_list}){if (_is_sep ($sep_item)){$sep=_parse_sep($sep_item)}else {push@seps,$sep;push@spec,_parse_spec($sep_item);undef$sep}}push@seps,$sep;my$title_form=_compile_field_format('title',\@seps);my$body_form=_compile_field_format('body',\@seps);my@titles=map {[@{$_->{title}}]}@spec;my$title_height=max(0,map {scalar(@$_)}@titles);for my$title (@titles){push @{$title},('')x ($title_height - @{$title})}for my$t_idx (0 .. $#titles){align($spec[$t_idx]->{align_title_lines},@{$titles[$t_idx]})}$tb->_spec(\@spec);$tb->_cols([map [],1 .. @spec]);$tb->_forms([$title_form,$body_form]);$tb->_titles(\@titles);$tb->_clear_cache;return$tb}sub _compile_format {my$seps=shift;for my$idx (0 .. $#$seps){if (!defined($seps->[$idx])){$seps->[$idx]=($idx==0 or $idx==$#$seps)? '' : q{ }}else {$seps->[$idx]=~ s/%/%%/g}}return join '%s',@$seps}sub _compile_field_format {my ($field,$seps)=@_;return _compile_format([map {defined($_)? $_->{$field}: undef}@$seps])}sub _recover_separators {my$format=shift;my@seps=split /(?<!%)%s/,$format,-1;for my$s (@seps){$s =~ s/%%/%/g}return \@seps}sub select {my$tb=shift;my@args=map$tb->_select_group($_),@_;my@sel=map$tb->_check_index($_),grep!_is_sep($_),@args;for my$arg (@args){if (!_is_sep($arg)){$arg=$tb->_spec->[$arg]}}my$sub=ref($tb)->new(@args);@{$sub->{cols}}=map {[@$_ ]}@{$tb->_cols}[@sel];$sub}sub _select_group {my ($tb,$group)=@_;return$group unless ref$group eq 'ARRAY';GROUP_LOOP: for my$g (@$group){if (_is_sep($g)){next GROUP_LOOP}$tb->_check_index($g);if (grep {$_}@{$tb->_cols->[$g]}){return @$group}return}return}sub _check_index {my$tb=shift;my ($i)=@_;my$n=$tb->n_cols;my$ok=eval {use warnings FATAL=>'numeric';-$n <= $i and $i < $n};_warn("Invalid column index '$_'")if $@ or not $ok;shift}sub _clear_cache {my ($tb)=@_;$tb->_blank(undef());$tb->_lines(undef());return}sub add {my$tb=shift;if (!$tb->n_cols){$tb->_entitle([('')x @_])}for my$row (_transpose([map {[defined()? split(/\n/): '' ]}@_ ])){$tb->_add(@$row)}$tb->_clear_cache;return$tb}sub _add {my$tb=shift;for my$col (@{$tb->_cols}){push @{$col},shift(@_)}$tb->_clear_cache;return$tb}sub load {my$tb=shift;for my$row (@_){if (!defined($row)){$row=''}$tb->add((ref($row)eq 'ARRAY')? (@$row): (split ' ',$row))}$tb}sub clear {my$tb=shift;for my$col (@{$tb->_cols}){$col=[]}$tb->_clear_cache;return$tb}sub n_cols {scalar @{$_[0]->{spec}}}sub title_height {$_[0]->n_cols and scalar @{$_[0]->_titles->[0]}}sub body_height {my ($tb)=@_;return ($tb->n_cols && scalar @{$tb->_cols->[0]})}sub table_height {my$tb=shift;return$tb->title_height + $tb->body_height}BEGIN {*height=\&table_height}sub width {my ($tb)=@_;return$tb->height && (length(($tb->table(0))[0])- 1)}sub _normalize_col_index {my ($tb,$col_index)=@_;$col_index ||= 0;if ($col_index < 0){$col_index += $tb->n_cols}if ($col_index < 0){$col_index=0}elsif ($col_index > $tb->n_cols){$col_index=$tb->n_cols}return$col_index}sub colrange {my ($tb,$proto_col_index)=@_;my$col_index=$tb->_normalize_col_index($proto_col_index);return (0,0)unless$tb->width;my@widths=map {length}@{$tb->_blank},'';@widths=@widths[0 .. $col_index];my$width=pop@widths;my$pos=sum(@widths)|| 0;my$seps_aref=_recover_separators($tb->_forms->[0]);my$sep_sum=0;for my$sep (@$seps_aref[0 .. $col_index]){$sep_sum += length($sep)}return ($pos+$sep_sum,$width)}sub table {my$tb=shift;return$tb->_table_portion($tb->height,0,@_)}sub title {my$tb=shift;return$tb->_table_portion($tb->title_height,0,@_)}sub body {my$tb=shift;return$tb->_table_portion($tb->body_height,$tb->title_height,@_)}sub stringify {my ($tb)=@_;return (scalar ($tb->table()))}sub _table_portion_as_aref {my$tb=shift;my$total=shift;my$offset=shift;my ($from,$n)=(0,$total);if (@_){$from=shift;$n=@_ ? shift : 1}($from,$n)=_limit_range($total,$from,$n);my$limit=$tb->title_height;$from += $offset;return [map$tb->_assemble_line($_ >= $limit,$tb->_table_line($_),0),$from .. $from + $n - 1 ]}sub _table_portion {my$tb=shift;my$lines_aref=$tb->_table_portion_as_aref(@_);return (wantarray ? @$lines_aref : join('',@$lines_aref))}sub _limit_range {my ($total,$from,$n)=@_;$from ||= 0;$from += $total if$from < 0;$n=$total unless defined$n;return (0,0)if$from + $n < 0 or $from >= $total;$from=0 if$from < 0;$n=$total - $from if$n > $total - $from;return($from,$n)}sub _table_line {my ($tb,$idx)=@_;if (!$tb->_lines){$tb->_lines([$tb->_build_table_lines ])}return$tb->_lines->[$idx]}sub _build_table_lines {my$tb=shift;my@cols=map {[map {defined($_)? $_ : ''}@$_ ]}@{$tb->_cols()};for my$col (@cols){push @$col,''}for my$col_idx (0 .. $#cols){push @{$cols[$col_idx]},@{$tb->_spec->[$col_idx]->{sample}}}for my$col_idx (0 .. $#cols){align($tb->_spec->[$col_idx]->{align},@{$cols[$col_idx]})}for my$col (@cols){splice(@{$col},1 + $tb->body_height)}for my$col_idx (0 .. $#cols){unshift @{$cols[$col_idx]},@{$tb->_titles->[$col_idx]}}for my$col_idx (0 .. $#cols){align($tb->_spec->[$col_idx]->{align_title},@{$cols[$col_idx]})}my@blank;for my$col (@cols){push@blank,pop(@$col)}$tb->_blank(\@blank);return _transpose_n($tb->height,\@cols)}sub _transpose_n {my ($n,$cols)=@_;return map {[map {shift @$_}@$cols]}1 .. $n}sub _transpose {my ($cols)=@_;my$m=max (map {scalar(@$_)}@$cols,[]);return _transpose_n($m,$cols)}sub _assemble_line {my ($tb,$in_body,$line_aref,$replace_spaces)=@_;my$format=$tb->_forms->[!!$in_body];if ($replace_spaces){$format =~ s/\s/=/g}return sprintf($format,@$line_aref)."\n"}sub _text_rule {my ($tb,$rule,$char,$alt)=@_;if (defined$alt){$rule =~ s/(.)/$1 eq ' ' ? $char : $alt/ge}else {$rule =~ s/ /$char/g if$char ne ' '}return$rule}sub _rule {my$tb=shift;return + (!$tb->width)? '' : $tb->_positive_width_rule(@_)}sub _positive_width_rule {my ($tb,$in_body,$char,$alt)=@_;my$rule=$tb->_assemble_line($in_body,$tb->_blank,((ref($char)eq "CODE")? 1 : 0),);return$tb->_render_rule($rule,$char,$alt)}sub _render_rule {my ($tb,$rule,$char,$alt)=@_;if (ref($char)eq "CODE"){return$tb->_render_rule_with_callbacks($rule,$char,$alt)}else {_default_if_empty(\$char,' ');return$tb->_text_rule($rule,$char,$alt)}}sub _get_fixed_len_string {my ($s,$len)=@_;$s=substr($s,0,$len);$s .= ' ' x ($len - length($s));return$s}sub _render_rule_with_callbacks {my ($tb,$rule,$char,$alt)=@_;my%callbacks=('char'=>{cb=>$char,idx=>0,},'alt'=>{cb=>$alt,idx=>0,},);my$calc_substitution=sub {my$s=shift;my$len=length($s);my$which=(($s =~ /\A /)? 'char' : 'alt');my$rec=$callbacks{$which};return _get_fixed_len_string(scalar ($rec->{cb}->($rec->{idx}++,$len)),$len,)};$rule =~ s/((.)\2*)/$calc_substitution->($1)/ge;return$rule}sub rule {my$tb=shift;return$tb->_rule(0,@_)}sub body_rule {my$tb=shift;return$tb->_rule(1,@_)}use Carp;{my ($warn,$fatal)=(0,0);sub warnings {my (undef,$toggle)=@_;$toggle ||= 'on';if ($toggle eq 'off'){($warn,$fatal)=(0,0)}elsif ($toggle eq 'fatal'){($warn,$fatal)=(1,1)}else {($warn,$fatal)=(1,0)}return$fatal ? 'fatal' : $warn ? 'on' : 'off'}sub _warn {my$msg=shift;return unless$warn;if ($fatal){croak($msg)}carp($msg);return}}
-TEXT_TABLE
-
 $fatpacked{"Text/Table/Any.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'TEXT_TABLE_ANY';
   package Text::Table::Any;our$DATE='2019-02-17';our$VERSION='0.095';our@BACKENDS=qw(Text::Table::Tiny Text::Table::TinyColor Text::Table::TinyColorWide Text::Table::TinyWide Text::Table::Org Text::Table::CSV Text::Table::TSV Text::Table::LTSV Text::Table::ASV Text::Table::HTML Text::Table::HTML::DataTables Text::Table::Paragraph Text::ANSITable Text::ASCIITable Text::FormatTable Text::MarkdownTable Text::Table Text::TabularDisplay Text::Table::XLSX Term::TablePrint);sub _encode {my$val=shift;$val =~ s/([\\"])/\\$1/g;"\"$val\""}sub backends {@BACKENDS}sub table {my%params=@_;my$rows=$params{rows}or die "Must provide rows!";my$backend=$params{backend}|| 'Text::Table::Tiny';my$header_row=$params{header_row}// 1;if ($backend eq 'Text::Table::Tiny'){require Text::Table::Tiny;return Text::Table::Tiny::table(rows=>$rows,header_row=>$header_row)."\n"}elsif ($backend eq 'Text::Table::TinyColor'){require Text::Table::TinyColor;return Text::Table::TinyColor::table(rows=>$rows,header_row=>$header_row)."\n"}elsif ($backend eq 'Text::Table::TinyColorWide'){require Text::Table::TinyColorWide;return Text::Table::TinyColorWide::table(rows=>$rows,header_row=>$header_row)."\n"}elsif ($backend eq 'Text::Table::TinyWide'){require Text::Table::TinyWide;return Text::Table::TinyWide::table(rows=>$rows,header_row=>$header_row)."\n"}elsif ($backend eq 'Text::Table::Org'){require Text::Table::Org;return Text::Table::Org::table(rows=>$rows,header_row=>$header_row)}elsif ($backend eq 'Text::Table::CSV'){require Text::Table::CSV;return Text::Table::CSV::table(rows=>$rows)}elsif ($backend eq 'Text::Table::TSV'){require Text::Table::TSV;return Text::Table::TSV::table(rows=>$rows)}elsif ($backend eq 'Text::Table::LTSV'){require Text::Table::LTSV;return Text::Table::LTSV::table(rows=>$rows)}elsif ($backend eq 'Text::Table::ASV'){require Text::Table::ASV;return Text::Table::ASV::table(rows=>$rows,header_row=>$header_row)}elsif ($backend eq 'Text::Table::HTML'){require Text::Table::HTML;return Text::Table::HTML::table(rows=>$rows,header_row=>$header_row)}elsif ($backend eq 'Text::Table::HTML::DataTables'){require Text::Table::HTML::DataTables;return Text::Table::HTML::DataTables::table(rows=>$rows,header_row=>$header_row)}elsif ($backend eq 'Text::Table::Paragraph'){require Text::Table::Paragraph;return Text::Table::Paragraph::table(rows=>$rows,header_row=>$header_row)}elsif ($backend eq 'Text::ANSITable'){require Text::ANSITable;my$t=Text::ANSITable->new(use_utf8=>0,use_box_chars=>0,use_color=>0,border_style=>'Default::single_ascii',);if ($header_row){$t->columns($rows->[0]);$t->add_row($rows->[$_])for 1..@$rows-1}else {$t->columns([map {"col$_"}0..$#{$rows->[0]}]);$t->add_row($_)for @$rows}return$t->draw}elsif ($backend eq 'Text::ASCIITable'){require Text::ASCIITable;my$t=Text::ASCIITable->new();if ($header_row){$t->setCols(@{$rows->[0]});$t->addRow(@{$rows->[$_]})for 1..@$rows-1}else {$t->setCols(map {"col$_"}0..$#{$rows->[0]});$t->addRow(@$_)for @$rows}return "$t"}elsif ($backend eq 'Text::FormatTable'){require Text::FormatTable;my$t=Text::FormatTable->new(join('|',('l')x @{$rows->[0]}));$t->head(@{$rows->[0]});$t->row(@{$rows->[$_]})for 1..@$rows-1;return$t->render}elsif ($backend eq 'Text::MarkdownTable'){require Text::MarkdownTable;my$out="";my$fields=$header_row ? $rows->[0]: [map {"col$_"}0..$#{$rows->[0]}];my$t=Text::MarkdownTable->new(file=>\$out,columns=>$fields);for (($header_row ? 1:0).. $#{$rows}){my$row=$rows->[$_];$t->add({map {$fields->[$_]=>$row->[$_]}0..@$fields-1 })}$t->done;return$out}elsif ($backend eq 'Text::Table'){require Text::Table;my$t=Text::Table->new(@{$rows->[0]});$t->load(@{$rows}[1..@$rows-1]);return$t}elsif ($backend eq 'Text::TabularDisplay'){require Text::TabularDisplay;my$t=Text::TabularDisplay->new(@{$rows->[0]});$t->add(@{$rows->[$_]})for 1..@$rows-1;return$t->render ."\n"}elsif ($backend eq 'Text::Table::XLSX'){require Text::Table::XLSX;return Text::Table::XLSX::table(rows=>$rows,header_row=>$header_row)}elsif ($backend eq 'Term::TablePrint'){require Term::TablePrint;my$rows2;if ($header_row){$rows2=$rows}else {$rows2=[@$rows];shift @$rows2}return Term::TablePrint::print_table($rows)}else {die "Unknown backend '$backend'"}}1;
 TEXT_TABLE_ANY
@@ -484,7 +485,7 @@ use strict;
 
 use App::Codeowners;
 
-our $VERSION = '0.41'; # VERSION
+our $VERSION = '0.42'; # VERSION
 
 App::Codeowners->main(@ARGV);
 
@@ -500,13 +501,15 @@ git-codeowners - A tool for managing CODEOWNERS files
 
 =head1 VERSION
 
-version 0.41
+version 0.42
 
 =head1 SYNOPSIS
 
     git-codeowners [--version|--help|--manual]
 
-    git-codeowners [show] [--format FORMAT] [--[no-]project] [PATH...]
+    git-codeowners [show] [--format FORMAT] [--owner OWNER]...
+                   [--pattern PATTERN]... [--[no-]patterns]
+                   [--project PROJECT]... [--[no-]projects] [PATH...]
 
     git-codeowners owners [--format FORMAT] [--pattern PATTERN]
 
@@ -602,18 +605,33 @@ Does not yet support Zsh...
 
 =head2 show
 
-    git-codeowners [show] [--format FORMAT] [--[no-]project] [PATH...]
+    git-codeowners [show] [--format FORMAT] [--owner OWNER]...
+                   [--pattern PATTERN]... [--[no-]patterns]
+                   [--project PROJECT]... [--[no-]projects] [PATH...]
 
 Show owners of one or more files in a repo.
 
+If C<--owner>, C<--project>, C<--pattern> are set, only show files with matching
+criteria. These can be repeated.
+
+Use C<--patterns> to also show the matching pattern associated with each file.
+
+By default the output might show associated projects if the C<CODEOWNERS> file
+defines them. You can control this by explicitly using C<--projects> or
+C<--no-projects> to always show or always hide defined projects, respectively.
+
 =head2 owners
 
     git-codeowners owners [--format FORMAT] [--pattern PATTERN]
 
+List all owners defined in the F<CODEOWNERS> file.
+
 =head2 patterns
 
     git-codeowners patterns [--format FORMAT] [--owner OWNER]
 
+List all patterns defined in the F<CODEOWNERS> file.
+
 =head2 create
 
     git-codeowners create [REPO_DIRPATH|CODEOWNERS_FILEPATH]
@@ -663,7 +681,7 @@ C<FORMAT> - Custom format (see below)
 
 =back
 
-=head2 Custom
+=head2 Format string
 
 You can specify a custom format using printf-like format sequences. These are the items that can be
 substituted:
@@ -724,7 +742,7 @@ C<nocolor> - Do not colorize replacement string.
 
 =back
 
-=head2 Table
+=head2 Format table
 
 Table formatting can be done by one of several different modules, each with its own features and
 bugs. The default module is L<Text::Table::Tiny>, but this can be overridden using the
@@ -734,6 +752,16 @@ C<PERL_TEXT_TABLE> environment variable if desired, like this:
 
 The list of available modules is at L<Text::Table::Any/@BACKENDS>.
 
+=head1 CAVEATS
+
+=over 4
+
+=item *
+
+Some commands require F<git> (at least version 1.8.5).
+
+=back
+
 =head1 BUGS
 
 Please report any bugs or feature requests on the bugtracker website
This page took 0.039952 seconds and 4 git commands to generate.