Rev 2615 | Blame | Compare with Previous | Last modification | View Log | RSS feed
################################################################################ Codestriker: Copyright (c) 2001,2002,2003 David Sitsky. All rights reserved.# sits@users.sourceforge.net## This program is free software; you can redistribute it and modify it under# the terms of the GPL.# Subversion repository access package.package Codestriker::Repository::Subversion;use IPC::Open3;use strict;use Fatal qw / open close /;# Constructor, which takes as a parameter the repository url.sub new {my ($type, $repository_url, $user, $password) = @_;# Determine if there are additional parameters required for user# authentication.my @userCmdLine = ();if (defined($user) && defined($password)) {push @userCmdLine, '--username';push @userCmdLine, $user;push @userCmdLine, '--password';push @userCmdLine, $password;}my $repotag = '';if ( $repository_url =~ m~(.*)\[(.*)\]$~ ){$repository_url = $1;$repotag = $2;}# Sanitise the repository URL.$repository_url = sanitise_url_component($repository_url);my $self = {};$self->{repository_url} = $repository_url;$self->{repotag} = $repotag;$self->{userCmdLine} = \@userCmdLine;$self->{repository_string} = $repository_url;$self->{repository_string} .= ";$user" if defined $user;$self->{repository_string} .= ";$password" if defined $password;if ($self->{repository_string} !~ /^svn:/) {$self->{repository_string} = "svn:" . $self->{repository_string};}bless $self, $type;}# Sanitise a Subversion URL component, by replacing spaces with %20 and @# symbols with %40, so that there is no confused with pegged revisions. Also# remove any leading and trailing slashes.sub sanitise_url_component {my $url = shift;$url =~ s/\/$//;$url =~ s/^\///;$url =~ s/ /%20/g;$url =~ s/\@/%40/g;return $url;}# Retrieve the data corresponding to $filename and $revision. Store each line# into $content_array_ref.sub retrieve ($$$\$) {my ($self, $filename, $revision, $content_array_ref) = @_;## Kludge# If the user has provided a Tag with a PEG, then the filename is# mangled. Eg:# MASS_Dev_Bus/Financial/cpp/Acquirer/tags/Acquirer_0.0.9000.bkk.1@24165/src/SetId/SetAcquirerIdConfig.cpp## Test Fix# Remove anyhing that looks like @nnnnn/#$filename =~ s~\@[0-9]+/~/~;# Sanitise the filename.$filename = sanitise_url_component($filename);my $read_data;my $read_stdout_fh = new FileHandle;open($read_stdout_fh, '>', \$read_data);my @args = ();push @args, 'cat';push @args, '--non-interactive';push @args, '--no-auth-cache';push @args, '--trust-server-cert';push @args, @{ $self->{userCmdLine} };push @args, '--revision';push @args, $revision;push @args, $self->{repository_url} . '/' . $filename;Codestriker::execute_command($read_stdout_fh, undef,$Codestriker::svn, @args);# Process the data for the topic.open($read_stdout_fh, '<', \$read_data);for (my $i = 1; <$read_stdout_fh>; $i++) {$_ = Codestriker::decode_topic_text($_);chop;$$content_array_ref[$i] = $_;}}# Retrieve the "root" of this repository.sub getRoot ($) {my ($self) = @_;return $self->{repository_url};}# Return a URL which views the specified file and revision.sub getViewUrl ($$$) {my ($self, $filename, $revision) = @_;# Lookup the file viewer from the configuration.my $viewer = $Codestriker::file_viewer->{$self->toString()};if (! (defined $viewer)) {$viewer = $Codestriker::file_viewer->{$self->{repository_string}};}return (defined $viewer) ? $viewer . "/" . $filename : "";}# Return a string representation of this repository.sub toString ($) {my ($self) = @_;return "svn:" . $self->getRoot();}# Given a Subversion URL, determine if it refers to a directory or a file.sub is_file_url {my ($self, $url) = @_;my $file_url;eval {my @args = ();push @args, 'info';push @args, '--non-interactive';push @args, '--no-auth-cache';push @args, '--trust-server-cert';push @args, @{ $self->{userCmdLine} };push @args, '--xml';push @args, $self->{repository_url} . '/' . $url;my $read_data;my $read_stdout_fh = new FileHandle;open($read_stdout_fh, '>', \$read_data);Codestriker::execute_command($read_stdout_fh, undef,$Codestriker::svn, @args);open($read_stdout_fh, '<', \$read_data);while (<$read_stdout_fh>) {if (/kind\s*\=\s*\"(\w+)\"/) {$file_url = $1 eq "File";last;}}};if ($@ || !(defined $file_url)) {# The above command failed, try using the older method which only works# in an English locale. This supports Subversion 1.2 or earlier# releases, which don't support the --xml flag for the info command.my @args = ();push @args, 'cat';push @args, '--non-interactive';push @args, '--no-auth-cache';push @args, '--trust-server-cert';push @args, @{ $self->{userCmdLine} };push @args, '--revision';push @args, 'HEAD';push @args, $self->{repository_url} . '/' . $url;my $read_stdout_data;my $read_stdout_fh = new FileHandle;open($read_stdout_fh, '>', \$read_stdout_data);my $read_stderr_data;my $read_stderr_fh = new FileHandle;open($read_stderr_fh, '>', \$read_stderr_data);Codestriker::execute_command($read_stdout_fh, $read_stderr_fh,$Codestriker::svn, @args);$file_url = 1;open($read_stderr_fh, '<', \$read_stderr_data);while(<$read_stderr_fh>) {if (/^svn:.* refers to a directory/) {$file_url = 0;last;}}}return $file_url;}# The getDiff operation, pull out a change set based on the start and end# revision number, confined to the specified moduled_name.sub getDiff {my ($self, $start_tag, $end_tag, $module_name, $stdout_fh, $stderr_fh) = @_;## If modulename conatins the repotag - then remove it#if ( $self->{repotag} ){if ( $module_name =~ m~$self->{repotag}/(.*)~ ){$module_name = $1;}}## Check Module Name#my @errors;$self->{getDiffError} = undef;unless ( $module_name =~ m~^(.*)/(tags|branches|trunk|Trunk)(/|$)~ ){push @errors,"Module does not contain 'tags', 'trunk' or 'branches'","Module must specify a development branch or trunk","ModuleName: $module_name";}my $moduleRoot = $1;my $moduleTail = $3;if ( $module_name =~ m~^/~ ){push @errors, "Module must not start with a '/'"}if ( @errors ){$self->{getDiffError} = join ('<br>', @errors);return $Codestriker::OK;}# Sanitise the URL, and determine if it refers to a directory or filename.$module_name = sanitise_url_component($module_name);my $directory;if ($self->is_file_url($module_name)) {$module_name =~ /(.*)\/[^\/]+/;$directory = $1;} else {$directory = $module_name;}## Detect mode of operation# 1) Two known revisions - simple (start and end specified)# 2) Revision to HEAD - simple (only start specified)# 3) Empty to Specified-Revision - tricky (only end specified)# 4) Error - detected before here (no start or end)#my $start_url;my $end_url;# my $moduleRoot = $module_name;# $moduleRoot =~ s~/trunk$~~;# $moduleRoot =~ s~/branches/.*~~;# $moduleRoot =~ s~/tags/.*~~;my $diffOld = $module_name;if ( ! $start_tag ){## Dummy start: Root of the empty repository#my $repoName = $module_name;$repoName =~ s~[/\\].*~~;$start_url = $repoName . '@0';}elsif ( $start_tag =~ m~^\d+$~ ){## Pegged version#$start_url = $module_name . '@' . $start_tag;}else{## Tagged version#$start_url = join ('/', $moduleRoot, 'tags', $start_tag);$diffOld = $start_url;}my $diffNew = $module_name;if ( ! $end_tag ){## Dummy end: Head of the branch#$end_url = $module_name;}elsif ( $end_tag =~ m~^\d+$~ ){## Pegged version#$end_url = $module_name . '@' . $end_tag;}else{## Tagged version#$end_url = join ('/', $moduleRoot, 'tags', $end_tag);$diffNew = $end_url;}# Execute the diff command.my $read_stdout_data;my $read_stdout_fh = new FileHandle;open($read_stdout_fh, '>', \$read_stdout_data);## Determine the command to use# If user provides two numbers, then module is a full path# Otherwise assume that the user has provided named tags##my @args = ();push @args, 'diff';push @args, '--non-interactive';push @args, '--no-auth-cache';push @args, '--trust-server-cert';push @args, @{ $self->{userCmdLine} };push @args, '--old';push @args, join ('/', $self->{repository_url},$start_url);push @args, '--new';push @args, join ('/', $self->{repository_url}, $end_url);Codestriker::execute_command($read_stdout_fh, $stderr_fh,$Codestriker::svn, @args);## DPurdie: 29-Nov-12# The diff output does not have the full file name# The following code attempts to fix this (bit it didn't work too well# with a mixture of tags, branches and pegs)## Unified Diff format :# --- /path/to/original# +++ /path/to/new# Svn Extension# Index: path/to/file##my $rv = open($read_stdout_fh, '<', \$read_stdout_data);if ( $rv ) {while(<$read_stdout_fh>) {my $line = $_;## If the user specifies a path (a branch in Subversion), the## diff file does not come back with a path rooted from the## repository base making it impossible to pull the entire file## back out. This code attempts to change the diff file on the## fly to ensure that the full path is present. This is a bug## against Subversion, so eventually it will be fixed, so this## code can't break when the diff command starts returning the## full path.#if ($line =~ /^--- / || $line =~ /^\+\+\+ / ||# $line =~ /^Index: /) {# # Check if the bug has been fixed.# if ($line =~ /^\+\+\+ $module_name/ == 0 &&# $line =~ /^--- $module_name/ == 0 &&# $line =~ /^Index: $module_name/ == 0) {# $line =~ s/^--- /--- $directory\// or# $line =~ s/^Index: /Index: $directory\// or# $line =~ s/^\+\+\+ /\+\+\+ $directory\//;# }#}$line =~ s~^Index: ~Index: $diffNew/~;$line =~ s~^--- ~--- $diffOld/~;$line =~ s~^\+\+\+ ~\+\+\+ $diffNew/~;print $stdout_fh $line;}}return $Codestriker::OK;}1;