diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2019-12-14 15:07:05 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2019-12-14 15:07:05 -0700 |
commit | d084cb8280396204df52cb133c5f3baaa547880c (patch) | |
tree | 6956b57968be7c7ca2a120925d0c5eb628d348ed /lib | |
parent | 5643ab990e364aa9bee62e76396a304efd09d306 (diff) | |
download | dotfiles-d084cb8280396204df52cb133c5f3baaa547880c.tar.gz |
implement interactive reviewing of unused files
Diffstat (limited to 'lib')
-rw-r--r-- | lib/perl5/Local/MrRepo/Repo/Git/Annex.pm | 152 |
1 files changed, 108 insertions, 44 deletions
diff --git a/lib/perl5/Local/MrRepo/Repo/Git/Annex.pm b/lib/perl5/Local/MrRepo/Repo/Git/Annex.pm index 6fbe616d..5d379ef9 100644 --- a/lib/perl5/Local/MrRepo/Repo/Git/Annex.pm +++ b/lib/perl5/Local/MrRepo/Repo/Git/Annex.pm @@ -22,10 +22,13 @@ use lib "$ENV{HOME}/lib/perl5"; use parent 'Local::MrRepo::Repo::Git'; use Exporter 'import'; +use File::Spec::Functions qw(rel2abs); use Git::Wrapper; use JSON; use Local::ScriptStatus; use Try::Tiny; +use Term::ReadKey; +use Local::Interactive qw(prompt); our @EXPORT_OK = (); @@ -59,56 +62,117 @@ sub review { my ($review_unused) = $self->git->config(qw(--local --get --type=bool --default true mrrepo.review-unused)); - if ($review_unused eq 'true') { - my @unused_lines = - $self->git->annex("unused", - { used_refspec - => "+refs/heads/*:-refs/heads/synced/*" }); - my @unused_files = - map { /^ ([0-9]+) +([^ ]+)$/ ? {number => $1, key => $2} : ()} - @unused_lines; - unless (@unused_files == 0) { - say_spaced_bullet("There are unused files you can drop with" - . " `git annex dropunused':"); - say for @unused_lines; + $issues = $self->review_unused(interactive => 0) || $issues + if $review_unused eq 'true'; + + return $issues; +} + +sub review_unused { + my $self = shift; + my %opts = @_; + $opts{interactive} //= 0; + $opts{used_refspec} //= "+refs/heads/*:-refs/heads/synced/*"; + + my %unused_args = (used_refspec => $opts{used_refspec}); + my %dropunused_args = (force => 1); + $unused_args{from} = $dropunused_args{from} = $opts{from} + if defined $opts{from}; + + my @to_drop = (); + my @unused_lines = $self->git->annex("unused", \%unused_args); + my @unused_files + = map { /^ ([0-9]+) +([^ ]+)$/ ? { number => $1, key => $2 } : () } + @unused_lines; + return 0 if @unused_files == 0; + unless ($opts{interactive}) { + say_spaced_bullet("There are unused files you can drop with" + . " `git annex dropunused':"); + say for @unused_lines; + say ""; + } + + foreach my $unused_file (@unused_files) { + system('clear', '-x') if $opts{interactive}; + say_bold("unused file #" . $unused_file->{number} . ":"); + + # this way of determining whether a file is tmp or bad, rather + # than really unused, is not great because it only works for + # the local repo and makes unneeded `ga contentlocation` + # calls. it would be better just to parse @unused_lines with + # a proper state machine and thereby extract into separate + # arrays the unused and the tmp/bad data + my $is_tmp_or_bad = 0; + my $content_location; + try { + ($content_location) + = $self->git->annex("contentlocation", $unused_file->{key}); + $content_location = rel2abs($content_location, $self->toplevel); + } + catch { + $is_tmp_or_bad = 1; + }; + + # $is_tmp_or_bad can only be trusted when we are operating on + # the local repo, so guard for that here + if ($is_tmp_or_bad && !defined $opts{from}) { + say " looks like stale tmp or bad file, with key " + . $unused_file->{key}; + } else { + # We need the RUN here to avoid special postprocessing but + # also to get the -c option passed -- unclear how to pass + # short options to git itself, not the 'log' subcommand, + # with Git::Wrapper except by using RUN (passing long + # options to git itself is easy, per Git::Wrapper docs) + my @log_lines = map { s/^/ /r } $self->git->RUN( + "-c", + "diff.renameLimit=3000", + "log", + { + stat => 1, + no_textconv => 1 + }, + "-3", + "--color=always", + "-S", + $unused_file->{key}); + + if ($opts{interactive}) { + # truncate log output if necessary to ensure user's + # terminal does not scroll + my (undef, $height, undef, undef) = GetTerminalSize(); + splice @log_lines, (($height - 5) - @log_lines) + if @log_lines > ($height - 5); + } say ""; - foreach my $unused_file (@unused_files) { - say_bold("unused file #" . $unused_file->{number} . ":"); - my $is_tmp_or_bad = 0; - my $content_location; - try { - ($content_location) = - $self->git->annex("contentlocation", $unused_file->{key}); - } catch { - $is_tmp_or_bad = 1; - }; - if ($is_tmp_or_bad) { - say " looks like stale tmp or bad file, with key " - . $unused_file->{key}; - } else { - # We need the RUN here to avoid special postprocessing but - # also to get the -c option passed -- unclear how to pass - # short options to git itself, not the 'log' subcommand, - # with Git::Wrapper except by using RUN (passing long - # options to git itself is easy, per Git::Wrapper docs) - my @log_lines = map { s/^/ /r } - $self->git->RUN("-c", "diff.renameLimit=3000", "log", - { - stat => 1, no_textconv => 1 }, - "-3", "--color=always", - "-S", $unused_file->{key}); - - say " " . $content_location; - say ""; - say for @log_lines; + say for @log_lines; + if ($opts{interactive}) { + my $response; + while (1) { + $response + = lc(prompt("Drop this unused file? (y/n/o)")); + if ($response eq 'y') { + push @to_drop, $unused_file->{number}; + last; + } elsif ($response eq 'n') { + last; + } elsif ($response eq 'o') { + system('xdg-open', $content_location); + } else { + say "invalid response"; + } } - say ""; } - $issues = 1; } + say ""; } - return $issues; + $self->git->annex("dropunused", \%dropunused_args, @to_drop) if @to_drop; + # return boolean value representing whether or not there are any + # unused files left after this run. note in non-interactive mode + # @to_drop will be empty so will always return 1 if we got this + # far in the subroutine + return @to_drop != @unused_files; } 1; |