#!/usr/bin/perl
# Copyright (C) 2005 Eric L. Wilhelm
use warnings;
use strict;
use IPC::Run qw(run);
use File::Basename;
use File::Path qw(rmtree);
our $svn = 'svn';
our $svk = 'svk';
our $sv = $svn; # XXX ick
($0 eq __FILE__) and exit main(@ARGV);
sub main {
my @dirs = @_;
# XXX this is bad? (it means if we get require()'d that we can't dispatch)
# XXX also, should I relink to myself to create all of my aliases?
my $mode = basename($0);
@dirs or die "usage: $mode
...\n";
($mode =~ s/^svn_//) or die;
my $func = dispatch($mode) or die "$mode is not one of ...";
run_loop(\@dirs, pattern => pattern($mode), method => $func);
0;
} # end subroutine main definition
########################################################################
sub run_loop {
my ($dirs, %opts) = @_;
my $func = $opts{method} or die;
my $pat = $opts{pattern};
##print "pattern: $pat (", ref($pat), "\n";
my @dirs = @$dirs;
foreach my $dir (@dirs) {
my ($in, $out, $err);
# XXX is this necessary/justified?
(-e $dir) or die "no such directory: $dir";
$sv = $svn;
{
# XXX subcommands should check/assert as needed
# only thing that requires dir to be a dir?
my $lookdir = ((-d $dir) ? $dir : dirname($dir)) . '/.svn';
(-d $lookdir) or ($sv = $svk);
}
run([$sv, 'status', $dir], \$in, \$out, \$err) or
die "ack ($dir) $? -> $err";
$out or next;
my @new = map({s/$pat\s*//;$_}
grep(/$pat/, split(/\n/, $out)));
@new or next;
## warn join("\n ", "new for $dir: ", @new), "\n";
$func->(@new);
}
} # end subroutine run_loop definition
########################################################################
sub dispatch {
my ($mode) = @_;
# list of functions which accept a list of files as their argument
my %table = (
adder => \&adder,
cleaner => \&cleaner,
diffs => \&diffs,
remover => \&remover,
ignorer => \&ignorer,
);
return($table{$mode}); # XXX do we even need this?
} # end subroutine dispatch definition
########################################################################
sub pattern {
my ($mode) = @_;
my %table = (
diffs => qr/^M.?/,
remover => qr/^!/,
);
return(exists($table{$mode}) ? $table{$mode} : qr/^\?/);
} # end subroutine pattern definition
########################################################################
sub adder {
my @new = @_;
system($sv, 'add', @new) and die "ack: $! ($?)";
} # end subroutine adder definition
########################################################################
sub remover {
my @gone = @_;
system($sv, 'rm', @gone) and die "ack: $! ($?)";
} # end subroutine remover definition
########################################################################
sub cleaner {
my @new = @_;
foreach my $item (@new) {
rmtree($item); # works on files or directories
}
} # end subroutine cleaner definition
########################################################################
sub diffs {
my @changed = @_;
# go through them backwards so they layer correctly
foreach my $item (reverse(@changed)) {
warn "$item\n";
run([$sv, 'diff', $item], '|',
[
qw(gvim - -R),
'--servername', "diff: $item",
'--cmd', ':cd /tmp',
'-c', ':set titlestring=',
'-c', ':set syntax=diff',
'-c', ':set nofoldenable',
'-c', ':map q :q!'
], '>', '/dev/null'
);
}
} # end subroutine diffs definition
########################################################################
sub ignorer {
my @created = @_;
# XXX this is broken -- doesn't account for the ?'d file being in a
# subdir
system($sv, 'propset',
'svn:ignore', join("\n", @created),
'.'
) and die "ack $! ($?)";
} # end subroutine ignorer definition
########################################################################
# vim:ts=2:sw=2:et:sta
1;