summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2019-12-14 15:07:05 -0700
committerSean Whitton <spwhitton@spwhitton.name>2019-12-14 15:07:05 -0700
commitd084cb8280396204df52cb133c5f3baaa547880c (patch)
tree6956b57968be7c7ca2a120925d0c5eb628d348ed /lib
parent5643ab990e364aa9bee62e76396a304efd09d306 (diff)
downloaddotfiles-d084cb8280396204df52cb133c5f3baaa547880c.tar.gz
implement interactive reviewing of unused files
Diffstat (limited to 'lib')
-rw-r--r--lib/perl5/Local/MrRepo/Repo/Git/Annex.pm152
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;