#!/usr/bin/perl
#
# blah blah... license info blah blah seems most other source files have this header comment format
#
# The Original Code was written for:
# Delta Debugging Framework.
#
# The Initial Developer of the Original Code:
# Richard Chu
#
# Contributor(s):
# Elizabeth Chak - Documentation
#
# Date Created: October 09, 2006
# Date Last Modified: October 24, 2006
#
# Version: 0.03
#
# Description:
# Light Wrapper around SVN commands used to manage, update, manipulate the SVN source repository.
# The wrapper assumes the most common usage of each command. It can perform the following commands:
# svn checkout
# svn update
# svn commit
# svn diff
#
# One thing I haven't figured out yet is if the svn commands return a return code to indicate
# whether or not the command executed successfully or not. If the svn commands do, then I
# would probably perform a check on whether or not the commands executed successfully or not.
#
# NOTE/FIXME: Currently (Oct. 24, 2006 version), the subroutines that take in revision numbers
# (update, diff) require that the argument be numerical. There is no support for
# revision keywords (HEAD, BASE, COMMITED, PREV), or revision dates as described
# here: http://svnbook.red-bean.com/nightly/en/svn.tour.revs.html
#
# Change History:
# October 09, 2006:
# - svn class created. Initial support for most common usage of checkout, update, and commit commands
#
# October 15, 2006:
# - added support for most common usage of diff command.
# - updated the update subroutine to receive another parameter for file/directory path.
# arguments in update subroutine are all optional now. 1st argument default is "HEAD".
#
# October 24, 2006:
# - fixed conditional statement in diff subroutine from < $nRevisions to <= $nRevisions
#
###############################################################################
use warnings;
use strict;
package RCS::SVN;
####
# Create an generic RCS element. The hash ($self) holds the values for svn
# url, username and password passed into the subroutine. Data type of
# hash is then changed to $class.
# @param url svn url
# @param username svn username
# @param password svn password
#
# Developer's comments:
# I think this class should ideally be designed as a singleton but I haven't figured
# out how to do that in Perl. Yet.
####
sub new ($$$) {
my $class = shift; # first element in @_ is class name
my ($url, $username, $password) = @_; # the rest are arguments passed in to subroutine
my $self = {
_url => $url,
_username => $username,
_password => $password
};
bless ($self, $class);
return $self;
};
####
# This is the destructor. It deletes the class variables that
# consist of svn url, svn username and svn password
####
sub destroy () {
my $self = shift;
delete $self->{_url};
delete $self->{_username};
delete $self->{_password};
}
####
# Checkout subroutine checks out the source tree from the svn source repository.
#
#
# @param directory Optional; Allows you to change the top-level directory of your
# working copy to be different than that in the SVN repository.
# The value must be relative position from your present working directory
# If it is undefined, it will default to empty string (top-level directory
# of working copy same as repository)
#
# Some interesting values:
# "." will make the top-level directory your present working directory
# "../" will make the top-level directory the parent directory of your present working directory.
# "C:\<directory>" will not work as expected as the value must be relative to present working directory.
####
sub checkout (;$) {
my $self = shift;
my $directory = pop;
if (defined($self->{_url})) {
if (!defined($directory)) {
$directory = "";
}
my $rc = `svn checkout $self->{_url} $directory`;
}
}
####
# Commit subroutine that commits changes to the source repository if the
# class variables for the svn username and svn password are defined.
# Argument passed in is the svn log message; can be an empty string but it's
# not recommended for logging purposes.
#
# @param message svn log message, describing the change
####
sub commit ($) {
my $message = pop;
my $self = shift;
if (defined($self->{_username}) && defined($self->{_password})) {
my $rc = `svn commit -m \"$message\" --username $self->{_username} --password $self->{_password}`;
}
else {
print "No username or password defined.";
}
}
####
# Update subroutine that updates your working copy to the source repository to receive changes
# made by other developers since your last update. An update will take files/ directories etc.
# on the server and copy it to your local copy. Update is only done if the username and password
# are defined and if revision numbers are not out of range. Otherwise, the user will be alerted about
# the error.
#
# @param revision Specified by argument passed in as long as it's valid (default is HEAD revision)
# Possible revision keywords: HEAD, (BASE?, COMMITTED?, PREV?) and numbers
# The number has to be less or equal to the number of revisions in the SVN source repository
# and can be acquired through getNumberOfRevisions() subroutine
#
# Revision keywords and definitions:
#
# HEAD
#
# The latest (or "youngest") revision in the repository.
#
# BASE
#
# The revision number of an item in a working copy. If the item has been locally modified, the
# "BASE version" refers to the way the item appears without those local modifications.
#
# COMMITTED
#
# The most recent revision prior to, or equal to, BASE, in which an item changed.
#
# PREV
#
# The revision immediately before the last revision in which an item changed.
# (Technically, COMMITTED - 1.)
#
# NOTE: PREV, BASE, and COMMITTED can be used to refer to local paths, but not to URLs.
#
# @param file Specified by second argument; file or directory is only updated if defined.
# @return rc svn update command
####
sub update (;$$) {
my $self = shift;
my ($revision, $file) = @_;
if (!defined($revision)) { $revision = "HEAD"; }
if (!defined($file)) { $file = ""; }
my $nRevisions = $self->getNumberOfRevisions();
if (defined($self->{_username}) && defined($self->{_password})
&& ($revision eq "HEAD" ||
$revision eq "BASE" ||
$revision eq "COMMITTED" ||
$revision eq "PREV" ||
($revision > 0 && $revision <= $nRevisions))) {
my $rc = `svn update -r $revision --username $self->{"_username"} --password $self->{"_password"} $file`;
}
else {
print "username or password undefined or revision number out of range. Must be between 1 and $nRevisions";
}
}
####
# Diff subroutine gets the differences between two paths ; optionally takes 3 parameters.
# If no arguments are given, then the user's working copy will be compared with the latest revision.
# If one argument is given (a revision number), then the working copy is compared with the passed in revision.
# If two arguments are given (2 revision numbers), then the first and second revision numbers passed in are compared.
# If a third option is given, then the differences between the file given is returned.
# The svn diff command will only perform if the username and password are defined.
#
# @param rev1 Revision number, working copy is compared with the passed in revision, has to be more than 0 and
# less or equal to the revision number retrieved from the number of revisions in the SVN source
# repository retrieved from getNumberOfRevisions subroutine
# @param rev2 Second revision number, first and second revision numbers passed in are compared, has to be more
# than 0 and less or equal to the revision number retrieved from the number of revisions in the SVN
# source repository retrieved from getNumberOfRevisions subroutine
# @param file Filename, differences between the file given is returned if this is defined
# @return rc svn diff command
####
sub diff (;$$$) {
my $self = shift;
my ($rev1, $rev2, $file) = @_;
my $rc;
my $nRevisions = $self->getNumberOfRevisions();
if (!defined($rev1)) { $rev1 = ""; }
if (!defined($rev2)) { $rev2 = ""; }
if (!defined($file)) { $file = ""; }
if (defined($self->{_username}) && defined($self->{_password})
&& ($rev1 eq "" || ($rev1 > 0 && $rev1 <= $nRevisions))
&& ($rev2 eq "" || ($rev2 > 0 && $rev2 <= $nRevisions))) {
if ($rev2 ne "") { $rev2 = ":" . $rev2; }
$rc = `svn diff -r $rev1$rev2 --username $self->{_username} --password $self->{_password} $file`;
}
else {
print "No username or password defined. Or revision number out of range.";
}
return $rc;
}
####
# getNumberOfRevisions subroutine returns the number of revisions in the SVN source repository
# if the class variables username and password are defined
#
# @return rc number of revisions in the SVN source repository
#
# Comments from developer:
# Did not find an svn command that returns the number of revisions so this is
# like a work around. May not be the best way but it is one way.
# This is supposed to be a private method. However, I have not figured out how to
# properly create private methods in perl (if it's even possible). Yet.
####
sub getNumberOfRevisions () {
my $self = shift;
my $rc = -1;
if (defined($self->{_username}) && defined($self->{_password})) {
my $log = `svn log -r HEAD --username $self->{"_username"} --password $self->{"_password"}`;
$log =~ m/r[0-9]+/;
my $r = $&;
$r =~ m/[0-9]+/;
$rc = $&;
}
return $rc;
}
1; # last expression of file must return true