SVN-S4
view release on metacpan or search on metacpan
$s4->{s4_binary} = $0;
DEBUG "Using '$s4->{s4_binary}' for s4 commands\n" if is_debug;
DEBUG "Using SVN_SSH='".($ENV{SVN_SSH}||'')."\n" if is_debug;
DEBUG "opts=", Dumper(\%opts), "\n" if is_debug>=9;
if ($Opt_Cmd eq '') {
# in svn, a few things work even with no subcommand
if ($Opt_Version) {
if (!$Opt_Quiet) {
print "s4 wrapper around subversion (Version $VERSION)\n";
print "by Bryce Denney and Wilson Snyder\n";
print "\n";
}
$s4->run("$s4->{svn_binary} --version " . ($Opt_Quiet?"--quiet" : ""));
exit 0;
} else {
die "%Error: Type 's4 help' for usage.\n";
}
}
# Execute comand
if ($Opt_Cmd eq "help-summary") {
print "All commands:\n";
cmd_help_summary();
} elsif ($Opt_Cmd eq "help") {
cmd_help();
} elsif ($Opt_Cmd eq "add") {
cmd_add();
} elsif ($Opt_Cmd eq "cat-or-mods") {
cmd_cat_or_mods();
} elsif ($Opt_Cmd eq "checkout") {
cmd_checkout();
} elsif ($Opt_Cmd eq "commit") {
cmd_commit();
} elsif ($Opt_Cmd eq "fixprop") {
cmd_fixprop();
} elsif ($Opt_Cmd eq "info-switches") {
cmd_info_switches();
} elsif ($Opt_Cmd eq "merge") {
cmd_merge();
} elsif ($Opt_Cmd eq "quick-commit") {
cmd_quick_commit();
} elsif ($Opt_Cmd eq "scrub") {
cmd_scrub();
} elsif ($Opt_Cmd eq "snapshot") {
cmd_snapshot();
} elsif ($Opt_Cmd eq "status") {
cmd_status();
} elsif ($Opt_Cmd eq "switch") {
cmd_switch();
} elsif ($Opt_Cmd eq "update") {
cmd_update();
} elsif ($Opt_Cmd eq "workpropdel") {
cmd_workpropdel();
} elsif ($Opt_Cmd eq "workpropget") {
cmd_workpropget();
} elsif ($Opt_Cmd eq "workproplist") {
cmd_workproplist();
} elsif ($Opt_Cmd eq "workpropset") {
cmd_workpropset();
} else {
cmd_svn();
}
#----------------------------------------------------------------------
sub usage {
print "Version $VERSION\n";
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT, -noperldoc=>1);
exit (1);
}
sub global_option_no_arg {
my $param = shift;
if ($param eq 'version') {
$Opt_Version = 1;
push @Opt_CmdParamsEnd, "--version";
} elsif ($param eq 'quiet') {
$Opt_Quiet = 1;
push @Opt_CmdParamsEnd, "--quiet";
} else {
push @Opt_CmdParamsEnd, "--$param";
}
}
sub global_option_with_arg {
my $param = shift;
my $value = shift;
push @Opt_CmdParamsEnd, "--$param", $value;
}
sub parameter {
my $param = shift;
if ($param =~ /^-/) {
if (!defined $Opt_Cmd) {
die "s4: %Error: Invalid global option: $param\n";
} else {
push @Opt_CmdParams, $param;
}
} else {
if (!defined $Opt_Cmd) {
$Opt_Cmd = $SvnOpt->dealias($param);
} else {
push @Opt_CmdParams, $param;
}
}
}
#######################################################################
#######################################################################
#######################################################################
# Commands invoked by the user
sub cmd_svn {
# Call s4 using all the default parameters from the command line
local $! = undef;
my $cmdref = raw_svn_command();
$s4->run(@{$cmdref});
}
# Usage: s4 snapshot PATH
sub cmd_snapshot {
DEBUG "cmd_snapshot ",join(' ',@Opt_CmdParams),"\n" if is_debug;
!$opts{unknown} or die "%Error: s4 snapshot: Unknown argument: $opts{unknown}[0]\n";
!defined($opts{path}[1]) or die "%Error: s4 snapshot: Only one path allowed: $opts{path}[1]\n";
#DEBUG "opts=",Dumper(\%opts), "\n" if is_debug;
my $path = _clean_path($opts{path}[0]);
$path = "." if !defined $path;
my $no_ignore = $opts{'--no-ignore'} || 0;
$s4->snapshot (path=>$path, disregard_ignore_list=>$no_ignore, scrub_cmd=>"\$S4 scrub");
}
# Usage: s4 scrub PATH
sub cmd_scrub {
DEBUG "cmd_scrub ",join(' ',@Opt_CmdParams),"\n" if is_debug;
!$opts{unknown} or die "%Error: s4 scrub: Unknown argument: $opts{unknown}[0]\n";
!defined($opts{path}[1]) or die "%Error: s4 scrub: Only one path allowed: $opts{path}[1]\n";
#DEBUG "opts=",Dumper(\%opts), "\n" if is_debug;
my $path = _clean_path($opts{path}[0]);
my $rev = ($opts{revision}[0]||$opts{pathrev}[0]);
if (!defined $path) {
print "%Error: s4 scrub requires the path of the area to scrub.\n";
die "%Error: s4 scrub: Please read the help messages very carefully before using scrub!";
}
###$path = "." if !defined $path; # make them type it
$s4->scrub (path=>$path,
revision=>$rev,
url=>$opts{url}[0],
verbose=>(!defined $opts{'--noverbose'}));
check_viewspec_after_svn_cmd(path=>$path,
revision=>$rev);
}
sub cmd_workpropdel {
DEBUG "cmd_workpropdel ",join(' ',@Opt_CmdParams),"\n" if is_debug;
!$opts{unknown} or die "%Error: s4 workpropdel: Unknown argument: $opts{unknown}[0]\n";
#DEBUG "opts=",Dumper(\%opts), "\n" if is_debug;
my $propname = $opts{propname}[0];
(defined $propname) or die "%Error: s4 workpropdel requires the property name\n";
$s4->workpropdel (propname=>$propname);
}
sub cmd_workpropget {
DEBUG "cmd_workpropget ",join(' ',@Opt_CmdParams),"\n" if is_debug;
!$opts{unknown} or die "%Error: s4 workpropget: Unknown argument: $opts{unknown}[0]\n";
#DEBUG "opts=",Dumper(\%opts), "\n" if is_debug;
my $propname = $opts{propname}[0];
(defined $propname) or die "%Error: s4 workpropget requires the property name\n";
$s4->workpropget (propname=>$propname, print=>1);
}
sub cmd_workproplist {
DEBUG "cmd_workpropget ",join(' ',@Opt_CmdParams),"\n" if is_debug;
!$opts{unknown} or die "%Error: s4 workpropget: Unknown argument: $opts{unknown}[0]\n";
#DEBUG "opts=",Dumper(\%opts), "\n" if is_debug;
$s4->workproplist (print=>1,
verbose=>$opts{'-v'},
xml=>$opts{'--xml'});
}
sub cmd_workpropset {
DEBUG "cmd_workpropset ",join(' ',@Opt_CmdParams),"\n" if is_debug;
!$opts{unknown} or die "%Error: s4 workpropset: Unknown argument: $opts{unknown}[0]\n";
#DEBUG "opts=",Dumper(\%opts), "\n" if is_debug;
my $propname = $opts{propname}[0];
my $propval = $opts{propval}[0];
(defined $propname) or die "%Error: s4 workpropset requires the property name\n";
(defined $propval) or die "%Error: s4 workpropset requires the property value\n";
$s4->workpropset (propname=>$propname, value=>$propval);
}
sub cmd_help {
my $cmd;
if ($opts{subcommand}) {
$cmd = $opts{subcommand}[0];
$cmd = $SvnOpt->dealias($cmd);
my $needdash;
if ($SvnOpt->command_s4_addition($cmd)
|| $SvnOpt->command_s4_changed($cmd)) {
print "s4 $cmd options:\n";
print $SvnOpt->command_help_summary($cmd);
print "\ns4 $cmd from manpage:\n";
_pod_section_for($cmd);
$needdash = 1;
}
if (!$SvnOpt->command_s4_addition($cmd)) {
print "\n","-"x70,"\n" if $needdash;
print "Svn Help:\n\n";
cmd_svn();
}
} else {
print "S4 Unique commands:\n";
print "\ts4 --help\n";
foreach my $cmd ($SvnOpt->commands_sorted) {
if ($SvnOpt->command_s4_addition($cmd)) {
printf "\ts4 %s\n", $cmd;
}
}
print "S4 Modified commands:\n";
foreach my $cmd ($SvnOpt->commands_sorted) {
if ($SvnOpt->command_s4_changed($cmd)) {
printf "\ts4 %s\n", $cmd;
}
}
print "\n","-"x70,"\n";
print "Svn Help:\n\n";
cmd_svn();
}
}
sub cmd_help_summary {
my $longest = 1;
foreach my $cmd ($SvnOpt->commands_sorted) {
$longest = length($cmd) if length($cmd)>$longest;
}
foreach my $cmd ($SvnOpt->commands_sorted) {
my $args = $SvnOpt->command_arg_text($cmd);
printf(" %-${longest}s %s\n",$cmd,$args);
}
}
#######################################################################
sub _pod_section_for {
my $cmd = shift;
#$SIG{__WARN__} = sub{}; #pod2text isn't clean.
#pod2text($0);
use Pod::Select;
When the script is done, the new working should match the original in every
respect. If anything prevents such a patch from being created, it will die
with an error. For example, if your working copy has deleted files or
directories, or other unhealthy things, the snapshot code may not know how to
recreate it so it will refuse to make a patch.
Snapshots can be useful for backing up your work (without having to check in),
for bug reporting, or any time you want to "save your state" so that you can
recreate your area later, or in another place.
Changes in text files appear in svn diff format. Changes in binary files are
TARred, base64 encoded, and the encoded text appears in the patch file.
I keep calling the output file a "patch" because in fact it can be used with
the patch program. But it's also a shell script that recreates the svn state
as well.
Example of making a snapshot and restoring:
s4 checkout -r22100 http://svn.collab.net/repos/svn/trunk/www svnwebsite
cd svnwebsite
# add some files, modify some files, svn update to other revisions
s4 rm images
cp index.html myindex.html
s4 add myindex.html
echo Finish my new favorite feature >> roadmap.html
echo as soon as possible >> roadmap.html
s4 snap > /tmp/snapshot
# The snapshot is a script to recreate these changes.
# Let's run it.
s4 revert -R . ; rm -f myindex.html # make it clean again
bash /tmp/snapshot
=head2 update [--top]
s4 update behaves exactly the same way as svn checkout, unless the
top directory that you update contains a file called Project.viewspec.
If Project.viewspec is present, s4 does the steps described in the
"checkout" section above.
In most updates, the viewspec file has not changed drastically, so there is
no need to redo the svn switches, and s4 will do svn update. But if the
tree structure changes, s4 will redo the switch commands.
With --top, update the highest subversion directory found at or above the
current directory, rather than the current directory itself.
=head2 workpropdel I<propname>
s4 workpropdel deletes a work-area property of the given name, if it
exists.
=head2 workpropget I<propname>
s4 workpropget returns a work-area property of the given name, if it
exists, otherwise "".
=head2 workproplist [--xml]
s4 workproplist lists all work area properties, with their values.
=head2 workpropset I<propname> I<propvalue>
s4 workpropset sets a work-area property of the given name to the given
value. Work area properties are associated and unique to a given work
area, and stored in the top level .svn directory.
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=item --orig
Pass all commands through to the original version of svn. Useful when svn
has been aliased to a different command.
=item --svn I<name>
Name of svn executable, or "svn" if not specified. See also S4_SVN.
=item --version
Displays program version and exits.
=back
=head1 VIEWSPEC FILES
A viewspec file is a text file containing a series of one-line commands.
Anything after a # character is considered a comment. Whitespace and blank
lines are ignored. The commands must be one of:
=over 4
=item set VAR VALUE
Set environment variable VAR to VALUE. This is useful for making
abbreviations to be used within the viewspec file, for frequently typed
things such as the name of the svn repository.
=item include FILE
Read another file that contains viewspec commands. If the filename does
not begin with a slash, it is considered to be relative to the directory
containing the Project.viewspec.
=item include URL
Read a file out of the SVN repository that contains viewspec commands.
=item view URL DIR
Directory DIR will be svn switched to URL.
=item view URL/(.*) DIR$1
Directory DIR will be svn switched to URL. URL may contain a parenthesized
regexp, which indicates the repository should be searched for matching
files/subdirectories with the matching name. If found, $1 will be
substituted into DIR. The URL parenthesis must follow all /s, that is they
( run in 0.534 second using v1.01-cache-2.11-cpan-71847e10f99 )