source: trunk/dasscm/dasscm@ 226

Last change on this file since 226 was 226, checked in by joergs, on Nov 20, 2007 at 4:44:28 PM

quoting filenames to handle filenames with spaces

  • Property keyword set to id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 18.9 KB
RevLine 
[186]1#!/usr/bin/perl -w
2
3# $Id: dasscm 226 2007-11-20 15:44:28Z joergs $
4
5use strict;
6
[208]7use Env
8 qw($DASSCM_PROD $DASSCM_REPO $USER $DASSCM_USERNAME $DASSCM_USER $DASSCM_PASSWORD);
[186]9use Cwd;
[214]10use Getopt::Long;
[186]11use File::Basename;
[209]12use File::Compare;
13use File::Copy;
[186]14use File::stat;
15use File::Path;
[214]16use Term::ReadKey;
[186]17
[205]18#
19# used ConfigFile instead of SmartClient::Config,
20# because the huge amount of SmartClient dependencies
21#use SmartClient::Config;
22use ConfigFile;
23
[189]24#####################################################################
25#
[186]26# global
[189]27#
[205]28
[215]29# file to store permissions
30my $permissions_file = "/etc/permissions.d/dasscm.permission_backup";
[220]31
[215]32# configuration file
[205]33my $config_file = "/etc/dasscm.conf";
[214]34my $config = ConfigFile::read_config_file($config_file);
[205]35my $DASSCM_LOCAL_REPOSITORY_BASE;
36my $DASSCM_REPOSITORY_NAME;
37my $DASSCM_SVN_REPOSITORY;
38
39my $SVN = "svn ";
40my $svnOptions = "";
41my $svnCheckoutCredentials = "";
42my $svnPasswordCredentials = "";
43
[196]44# command line options get stored in options hash
[205]45my %options = ();
46
[197]47# subcommand, that gets executed (add, commit, ...)
[196]48my $command;
[186]49
[205]50my $verbose = 0;
51
[189]52#####################################################################
53#
[186]54# util functions
[189]55#
[187]56sub usage()
57{
[205]58 print "usage: dasscm <subcommand> [options] [args]\n";
59 print "\n";
60 print "dasscm is intended to help versioning configuration files\n";
61 print "\n";
62 print "Available subcommands:\n";
[215]63 print " help <subcommand>\n";
[205]64 print " init\n";
65 print " login\n";
[215]66 print " up\n";
67 print " ls\n";
[205]68 print " add <filename>\n";
69 print " commit <filename>\n";
[214]70 print " status <filename>\n";
[205]71 print " diff <filename>\n";
[215]72 print " permissions\n";
[205]73 print "\n";
[220]74 print "preperation:\n", " if dasscm is already configured,\n",
75 " use 'dasscm login' and than eg. 'add'.\n",
76 " The environment variables\n", " DASSCM_REPO\n", " DASSCM_PROD\n",
77 " DASSCM_USERNAME\n", " DASSCM_PASSWORD\n",
78 " are evaluated, but set automatically by 'dasscm login'.\n", "\n",
79 " If dasscm is not yet configured, read",
80 " /usr/share/doc/packages/dasscm/dasscm_howto.txt\n";
[187]81}
82
[186]83sub check_env()
84{
[205]85
86 # DASSCM_PROD
87 if ( !$DASSCM_PROD ) {
88 $DASSCM_PROD = "/";
89 }
90
91 if ( !-d $DASSCM_PROD ) {
92 die "DASSCM_PROD ($DASSCM_PROD) is not set to a directory.\n";
93 }
94 if ($verbose) { print "DASSCM_PROD: " . $DASSCM_PROD . "\n"; }
95
96 # DASSCM_REPOSITORY_NAME
[208]97 if ( !$DASSCM_REPOSITORY_NAME ) {
98 die
99 "Variable DASSCM_REPOSITORY_NAME is not defined.\nIt needs to be a unique name.\nNormally the full qualified host name is used.\nUse file $config_file to configure it.\n";
100 }
[205]101
102 # DASSCM_REPO
103 if ( !$DASSCM_REPO ) {
104 if ( $DASSCM_LOCAL_REPOSITORY_BASE && $DASSCM_REPOSITORY_NAME ) {
105 $DASSCM_REPO =
106 $DASSCM_LOCAL_REPOSITORY_BASE . "/" . $DASSCM_REPOSITORY_NAME;
107 } else {
108 die
109 "Envirnonment variable DASSCM_REPO not set.\nSet DASSCM_REPO to the directory of the versioning system checkout for this machine.\n";
110 }
111 }
[215]112 if ($verbose) { print "DASSCM_REPO: " . $DASSCM_REPO . "\n"; }
[205]113
114 #
115 # check if local repository directory exist (if not creating by init)
116 #
117 if ( $command ne "init" ) {
118 if ( not -d $DASSCM_REPO ) {
119 die
[208]120 "Can't access local repository DASSCM_REPO\n($DASSCM_REPO)\nCheck configuration and execute\n dasscm init\n";
[205]121 }
[208]122
[205]123 #
124 # user settings
125 #
[208]126
[205]127 # DASSCM_USER is legacy. Use DASSCM_USERNAME instead
[208]128 if ( !$DASSCM_USERNAME ) {
129 $DASSCM_USERNAME = $DASSCM_USER;
[205]130 }
131
132 # user root is not allowed for checkins.
133 # if user is root, DASSCM_USER has to be set,
134 # otherwise USER can be used
135 if ( "$USER" eq "root" ) {
[208]136 if ( ( not $DASSCM_USERNAME ) and ( $command ne "login" ) ) {
[205]137 die
[208]138 "Envirnonment variable DASSCM_USERNAME not set.\nSet DASSCM_USERNAME to your subversion user account.\n";
[205]139 }
140 $svnOptions .= " --no-auth-cache ";
141 } elsif ( !$DASSCM_USERNAME ) {
142 $DASSCM_USERNAME = $USER;
143 }
144
145 #
146 # password
147 #
[208]148 if ($DASSCM_PASSWORD) {
[205]149 $svnPasswordCredentials = " --password $DASSCM_PASSWORD ";
150 }
151 }
152
153 #$svnOptions .= " --username $DASSCM_USERNAME "
[186]154}
155
156sub check_parameter(@)
157{
158}
159
[187]160sub get_filenames(@)
161{
[205]162 my $filename_prod = $_[0];
163 if ( !( $filename_prod =~ m/^\// ) ) {
164 $filename_prod = cwd() . "/" . $filename_prod;
165 }
[187]166
[205]167 -r $filename_prod or die "$filename_prod is not accessable";
168
169 # TODO: dirname buggy: eg. "/etc/" is reduced to "/",
170 # "/etc" is used as filename
171 my $dirname_prod = dirname($filename_prod);
172 chdir $dirname_prod or die $!;
173 $dirname_prod = cwd();
174 my $basename = basename($filename_prod);
175
[214]176 if ($verbose) {
177 print "dir: " . $dirname_prod . "\n";
178 print "fn: " . $basename . "\n";
179 }
[205]180
181 my $dirname_repo = $DASSCM_REPO . "/" . $dirname_prod;
182 my $filename_repo = "$dirname_repo/$basename";
183
184 return (
185 $basename, $dirname_prod, $dirname_repo,
186 $filename_prod, $filename_repo
187 );
[187]188}
189
[215]190sub generatePermissionList
[209]191{
192
[215]193 # generieren der Zeilen für Permission-Savefile
194 my @files = @_;
195 my @permlist = ();
196 foreach my $file (@files) {
197 my $info = stat( "/" . $file ) || die "$file: stat error";
198 my $mode = get_type( $info->mode ) & 07777;
199 my $modestring = sprintf( "%04o", $mode );
200 my $uid = $info->uid;
201 my $uidname = getpwuid($uid);
202 my $gid = $info->gid;
203 my $gidname = getgrgid($gid);
204 push(
205 @permlist,
206 sprintf( "%-55s %-17s %4d",
207 $file, "${uidname}:${gidname}", $modestring )
208 );
209 }
210 return @permlist;
211}
[209]212
[215]213sub get_type
214{
[209]215
[215]216 # Funktion übernommen aus /usr/bin/chkstat
217 my $S_IFLNK = 0120000; # symbolic link
218 my $S_IFREG = 0100000; # regular file
219 my $S_IFDIR = 0040000; # directory
220 my $S_IFCHAR = 0020000; # character device
221 my $S_IFBLK = 0060000; # block device
222 my $S_IFFIFO = 0010000; # fifo
223 my $S_IFSOCK = 0140000; # socket
224 my $S_IFMT = 0170000; # type of file
[209]225
[215]226 my $S_m;
227 if ( ( $_[0] & $S_IFMT ) == $S_IFLNK ) { $S_m = $_[0] - $S_IFLNK; }
228 elsif ( ( $_[0] & $S_IFMT ) == $S_IFREG ) { $S_m = $_[0] - $S_IFREG; }
229 elsif ( ( $_[0] & $S_IFMT ) == $S_IFDIR ) { $S_m = $_[0] - $S_IFDIR; }
230 elsif ( ( $_[0] & $S_IFMT ) == $S_IFCHAR ) { $S_m = $_[0] - $S_IFCHAR; }
231 elsif ( ( $_[0] & $S_IFMT ) == $S_IFBLK ) { $S_m = $_[0] - $S_IFBLK; }
232 elsif ( ( $_[0] & $S_IFMT ) == $S_IFFIFO ) { $S_m = $_[0] - $S_IFFIFO; }
233 elsif ( ( $_[0] & $S_IFMT ) == $S_IFSOCK ) { $S_m = $_[0] - $S_IFSOCK; }
234 $S_m;
[209]235}
236
[186]237sub run_command
238{
[205]239 my $command = shift;
[186]240
[205]241 #print "executing command: " . $command . "\n";
[186]242
[205]243 open( RESULT, $command . ' 2>&1 |' );
244 my @result = <RESULT>;
245 close(RESULT);
246 my $retcode = $? >> 8;
[186]247
[205]248 #print @result;
249 #if( $retcode ) { print "return code: " . $retcode . "\n"; }
[186]250
[205]251 return ( $retcode, @result );
[186]252}
253
[205]254sub run_interactive
255{
[186]256
[208]257 if ($verbose) {
[205]258 print "run_interactive:" . join( " ", @_ ) . "\n";
259 }
[196]260
[205]261 system(@_);
262 if ( $? == -1 ) {
263 printf "failed to execute: $!\n";
264 } elsif ( $? & 127 ) {
265 printf "child died with signal %d, %s coredump\n", ( $? & 127 ),
266 ( $? & 128 ) ? 'with' : 'without';
267 } elsif ( $? >> 8 != 0 ) {
268 printf "child exited with value %d\n", $? >> 8;
269 }
270 return ( $? >> 8 );
271}
272
273sub svn_check_credentials( $$ )
[196]274{
[205]275 my $username = shift;
276 my $password = shift;
277
[220]278 print "checking credentials ... ";
279
280 # Options for "svn info" are not supported by subversion 1.0.0 (SLES9),
281 # therefore switching to "svn status"
282 # ( my $rc_update, my @result ) =
283 # run_command(
284 # "$SVN info --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
285 # );
286 #print @result;
287
[205]288 ( my $rc_update, my @result ) =
289 run_command(
[220]290 "$SVN ls --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
[205]291 );
292
293 if ( $rc_update != 0 ) {
[222]294 print @result;
[205]295 die;
296 }
297
[196]298}
299
[205]300sub svn_update( ;$ )
301{
302 my $update_path = shift || $DASSCM_REPO;
303 ( my $rc_update, my @result ) =
[220]304 run_command(
305 "$SVN update --non-interactive $svnCheckoutCredentials $update_path");
[215]306 print @result;
[205]307 if ( $rc_update != 0 ) {
308 die;
309 }
[215]310}
[196]311
[215]312sub svn_getStoredFiles( ;$ )
313{
[220]314
[215]315 # TODO: get_filenames?
316 #my $rel_path = shift || "";
317 #my $path = "${DASSCM_REPO}/${rel_path}";
318 my $path = ${DASSCM_REPO};
[220]319
[218]320 # svn ls -R is better, but much, much slower
321 # ( my $rc, my @result ) = run_command("$SVN ls --recursive $svnCheckoutCredentials $path");
[220]322 ( my $rc, my @result ) =
323 run_command(
324 "cd $path && find | grep -v '/.svn' | sed -e 's/\.\\///' | grep -v '^\$'"
325 );
[215]326 if ( $rc != 0 ) {
327 print @result;
328 die;
329 }
330 chomp(@result);
331 return @result;
[205]332}
333
[189]334#####################################################################
335#
[186]336# functions
337
338sub help(;@)
339{
[205]340 if ( @_ == 0 ) {
341 usage();
342 } else {
343 print "help for @_: ...\n";
[214]344 usage();
[205]345 }
[186]346}
347
[203]348sub login(@)
349{
[205]350 check_parameter( @_, 1 );
351 check_env();
[203]352
[214]353 my $input_username = $1;
354
355 if ( not $input_username ) {
356 my $output_username = "";
357 if ($DASSCM_USERNAME) {
358 $output_username = " ($DASSCM_USERNAME)";
359 }
360
361 print "Enter DASSCM user name", $output_username, ": ";
362 $input_username = <STDIN>;
363 chomp($input_username);
[205]364 }
[203]365
[205]366 # hidden password input
367 print "Enter DASSCM user password: ";
368 ReadMode('noecho');
369 my $input_password = <STDIN>;
370 ReadMode('normal');
371 chomp($input_password);
[220]372 print "\n";
[203]373
[205]374 svn_check_credentials( $input_username, $input_password );
375
376 #
377 # set environment variables
378 #
379 $ENV{'DASSCM_USERNAME'} = $input_username;
380 $ENV{'DASSCM_PASSWORD'} = $input_password;
381
[209]382 print "subversion access okay\n\n", "DASSCM_USERNAME: $input_username\n",
383 "DASSCM_PASSWORD: (hidden)\n", "DASSCM_PROD: $DASSCM_PROD\n",
384 "DASSCM_REPO: $DASSCM_REPO\n",
385 "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n", "[dasscm shell]\n\n";
[205]386
[209]387 exec("bash") or die "failed to start new shell";
[203]388}
389
[205]390sub init(@)
391{
392 check_parameter( @_, 1 );
393 check_env();
394
395 # update complete repository
[216]396 # and create permission file
[208]397 my $retcode =
398 run_interactive(
[225]399 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; touch $permissions_file"
[208]400 );
[205]401}
402
[215]403sub ls(@)
[186]404{
[205]405 check_parameter( @_, 1 );
406 check_env();
[186]407
[215]408 my @files = svn_getStoredFiles(@_);
409
410 print join( "\n", @files );
411 print "\n";
412}
413
414sub update(@)
415{
416 check_parameter( @_, 1 );
417 check_env();
418
419 #
420 # update local repository
421 #
422 svn_update();
423}
424
425sub add_helper(@)
426{
[205]427 (
428 my $basename,
429 my $dirname_prod,
430 my $dirname_repo,
431 my $filename_prod,
432 my $filename_repo
433 )
434 = get_filenames( $_[0] );
[186]435
[205]436 if ( $command eq "add" ) {
437 mkpath($dirname_repo);
438 }
[186]439
[205]440 copy( $filename_prod, $filename_repo ) or die $!;
441
442 if ( $command eq "add" ) {
443
444 # already checked in?
445 chdir($DASSCM_REPO);
446
447 # also add the path to filename.
448 for my $dir ( split( '/', $dirname_prod ) ) {
449 if ($dir) {
[226]450 my( $rc, @out ) = run_command("$SVN add --non-recursive \"" . $dir . "\"" );
451 if( $rc > 0 ) {
452 print join( "\n", @out );
453 }
[205]454 chdir $dir;
455 }
456 }
[226]457 my( $rc, @out ) = run_command("$SVN add \"" . $basename . "\"");
458 if( $rc > 0 ) {
459 print join( "\n", @out );
460 }
[205]461 }
[215]462}
[205]463
[215]464#
465# add (is used for command add and commit)
466#
467sub add(@)
468{
469 check_parameter( @_, 1 );
470 check_env();
471
472 #
473 # update local repository
474 #
475 svn_update();
476
477 # add file
478 add_helper( $_[0] );
[220]479
[215]480 # create new permissions file
481 permissions();
[220]482
[215]483 # add permissions file
[220]484 add_helper($permissions_file);
[215]485
[205]486 if ( $options{'message'} ) {
487 $svnOptions .= " --message \"$options{'message'}\" ";
488 }
489
490 # commit calls $EDITOR. uses "interactive" here, to display output
[215]491 my $retcode =
[205]492 run_interactive(
[208]493 "$SVN commit $svnOptions --username $DASSCM_USERNAME $svnPasswordCredentials $DASSCM_REPO"
494 );
[205]495
[215]496 #print $filename_prod. "\n";
497 #print $dirname_repo. "\n";
[186]498}
499
[193]500sub blame(@)
501{
[205]502 check_parameter( @_, 1 );
503 check_env();
[193]504
[205]505 (
506 my $basename,
507 my $dirname_prod,
508 my $dirname_repo,
509 my $filename_prod,
510 my $filename_repo
511 )
512 = get_filenames( $_[0] );
513
514 my $retcode = run_interactive("$SVN blame $svnOptions $filename_repo");
[193]515}
516
[187]517sub diff(@)
518{
[205]519 check_parameter( @_, 1 );
520 check_env();
[187]521
[205]522 (
523 my $basename,
524 my $dirname_prod,
525 my $dirname_repo,
526 my $filename_prod,
527 my $filename_repo
528 )
529 = get_filenames( $_[0] );
530
531 #print "$basename,$dirname_prod,$dirname_repo\n";
532
533 ( my $rc_update, my @result ) = run_command("$SVN update $filename_repo");
534 if ( $rc_update != 0 ) {
535 print @result;
536 die;
537 }
538
539 ( my $rc_diff, my @diff ) =
540 run_command("diff $filename_repo $filename_prod");
541 print @diff;
[187]542}
543
[209]544sub status(@)
545{
546 check_parameter( @_, 1 );
547 check_env();
548
549 #
550 # update local repository
551 #
552 svn_update();
553
554 # TODO: start at subdirectories ?
[220]555 my $dir = $DASSCM_REPO;
[215]556 my @files = svn_getStoredFiles($dir);
[209]557
558 # Liste der geänderten Files ausgeben, falls nicht leer
[215]559 if (@files) {
[209]560
[215]561 # stores result from status (cvscheck)
562 my %removedfiles = ();
563 my %changedfiles = ();
564
565 foreach my $file (@files) {
566
567 my $realfile = "/" . $file;
568 my $cvsworkfile = "${DASSCM_REPO}/${file}";
569
570 if ( -d $realfile ) {
571
572 # directory. do nothing
573 } elsif ( !-r $realfile ) {
574 $removedfiles{"$realfile"} = $cvsworkfile;
575 } else {
576 ( -r "$cvsworkfile" )
577 || die("Fehler: $cvsworkfile ist nicht lesbar");
578 if ( compare( $cvsworkfile, $realfile ) != 0 ) {
579 $changedfiles{"$realfile"} = $cvsworkfile;
580 }
581 }
582 }
583
584 if (%removedfiles) {
585 print "deleted files (found in repository, but not in system):\n";
586 foreach my $key ( values %removedfiles ) {
[209]587 print "$key\n";
588 }
589 print "\n";
590 }
591
[215]592 if (%changedfiles) {
[209]593 print "modified files:\n";
[215]594 foreach my $key ( keys %changedfiles ) {
[209]595 print "$key\n";
596 }
597 }
598 } else {
[215]599 print "no modified files found in $dir\n";
[209]600 }
[215]601
[209]602 print "\n";
[215]603}
[209]604
[215]605sub permissions(@)
606{
607 check_parameter( @_, 1 );
608 check_env();
609
610 #
611 # update local repository
612 #
613 #svn_update();
614
615 # TODO: start at subdirectories ?
[220]616 my $dir = $DASSCM_REPO;
[215]617 my @files = svn_getStoredFiles($dir);
618
619 if (@files) {
620
621 # generieren der Permissions
622 my @permissions = generatePermissionList(@files);
623 my $OUTFILE;
624 my $tofile = 0; # Status für schreiben in File
[220]625
[215]626 if ( -w dirname($permissions_file) ) {
627
628 # Verzeichnis existiert => schreiben
629 print "storing permissions in file $permissions_file\n";
630 open( OUTFILE, ">$permissions_file" )
[216]631 || die("failed to write to $permissions_file: $!");
[215]632 $tofile = 1; # Merken, daß in File geschrieben wird
633 print OUTFILE "#\n";
634 print OUTFILE "# created by dasscm permissions\n";
[220]635 print OUTFILE
636 "# It is intended to be used for restoring permissions\n";
[215]637 } else {
638
639 # Pfad für Sicherungsdatei existiert nicht => schreiben auf stdout
640 # Alias Filehandle für stdout erzeugen
641 *OUTFILE = *STDOUT;
642 }
643 foreach my $line (@permissions) {
644 print OUTFILE "$line\n";
645 }
646
[220]647 if ($tofile) {
[215]648 close(OUTFILE);
649 }
650 }
[209]651}
652
[189]653#####################################################################
654#
[186]655# main
[189]656#
[186]657
658my $number_arguments = @ARGV;
659
[205]660if ( $number_arguments > 0 ) {
[186]661
[205]662 # get subcommand and remove it from @ARGV
663 $command = $ARGV[0];
664 shift @ARGV;
[196]665
[205]666 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
667 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
[196]668
[205]669 # TODO: check variables
670 $DASSCM_SVN_REPOSITORY =
671 $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
672
673 my $DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
674 my $DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
675
676 #
677 # if a user is given by dasscm configuration file, we use it.
678 # Otherwise we expect that read-only account is configured
679 # as local subversion configuration.
680 # If this is also not the case,
681 # user is required to type username and password.
682 # This will be stored as local subversion configuration thereafter.
683 #
684 if ( $DASSCM_CHECKOUT_USERNAME && $DASSCM_CHECKOUT_PASSWORD ) {
685 $svnCheckoutCredentials =
686 " --username $DASSCM_CHECKOUT_USERNAME --password $DASSCM_CHECKOUT_PASSWORD ";
687 }
688
689 # get command line options and store them in options hash
[214]690 my $result = GetOptions( \%options, 'verbose', 'message=s' );
[205]691
692 # print options
693 foreach my $option ( keys %options ) {
[215]694 print "${option}: $options{$option}\n";
[205]695 }
696
[214]697 # set verbose to command line option
698 $verbose = $options{'verbose'};
699
700 #
701 # action accordinly to command are taken
702 # $command is rewritten in standard format,
703 # so we can test for it later on more simply
704 #
[205]705 $_ = $command;
706 if (m/help/i) {
707 help(@ARGV);
708 } elsif (m/login/i) {
[208]709 $command = "login";
[205]710 login(@ARGV);
711 } elsif (m/init/i) {
[208]712 $command = "init";
[205]713 init(@ARGV);
[215]714 } elsif (m/ls/i) {
715 $command = "ls";
716 ls(@ARGV);
717 } elsif (m/up/i) {
718 $command = "update";
719 update(@ARGV);
[205]720 } elsif (m/add/i) {
721 $command = "add";
722 add(@ARGV);
723 } elsif (m/commit/i) {
724 $command = "commit";
725 add(@ARGV);
726 } elsif (m/blame/i) {
[208]727 $command = "blame";
[205]728 blame(@ARGV);
729 } elsif (m/diff/i) {
[208]730 $command = "diff";
[205]731 diff(@ARGV);
[209]732 } elsif (m/status/i) {
733 $command = "status";
734 status(@ARGV);
[215]735 } elsif (m/permissions/i) {
736 $command = "permissions";
737 permissions(@ARGV);
[205]738 } else {
[215]739 print "unknown command: $command\n\n";
[205]740 usage();
741 check_env();
742 }
743
[209]744 # cleanup (svn-commit.tmp)
[205]745 # commitall
746 # revert
[215]747 # activate
[186]748}
Note: See TracBrowser for help on using the repository browser.