Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1293 dpurdie 1
###############################################################################
2
# Codestriker: Copyright (c) 2001,2002,2003 David Sitsky.  All rights reserved.
3
# sits@users.sourceforge.net
4
#
5
# This program is free software; you can redistribute it and modify it under
6
# the terms of the GPL.
7
 
8
# Subversion repository access package.
9
 
10
package Codestriker::Repository::Subversion;
11
use IPC::Open3;
12
 
13
use strict;
14
use Fatal qw / open close /;
15
 
16
# Constructor, which takes as a parameter the repository url.
17
sub new {
18
    my ($type, $repository_url, $user, $password) = @_;
19
 
20
    # Determine if there are additional parameters required for user
21
    # authentication.
22
    my @userCmdLine = ();
23
    if (defined($user) && defined($password)) {
24
        push @userCmdLine, '--username';
25
	push @userCmdLine, $user;
26
	push @userCmdLine, '--password';
27
	push @userCmdLine, $password;
28
    }
29
 
30
    # Sanitise the repository URL.
31
    $repository_url = sanitise_url_component($repository_url);
32
 
33
    my $self = {};
34
    $self->{repository_url} = $repository_url;
35
    $self->{userCmdLine} = \@userCmdLine;
36
    $self->{repository_string} = $repository_url;
37
    $self->{repository_string} .= ";$user" if defined $user;
38
    $self->{repository_string} .= ";$password" if defined $password;
39
    if ($self->{repository_string} !~ /^svn:/) {
40
	$self->{repository_string} = "svn:" . $self->{repository_string};
41
    }
42
 
43
    bless $self, $type;
44
}
45
 
46
# Sanitise a Subversion URL component, by replacing spaces with %20 and @
47
# symbols with %40, so that there is no confused with pegged revisions.  Also
48
# remove any leading and trailing slashes.
49
sub sanitise_url_component {
50
    my $url = shift;
51
    $url =~ s/\/$//;
52
    $url =~ s/^\///;
53
    $url =~ s/ /%20/g;
54
    $url =~ s/\@/%40/g;
55
    return $url;
56
}
57
 
58
# Retrieve the data corresponding to $filename and $revision.  Store each line
59
# into $content_array_ref.
60
sub retrieve ($$$\$) {
61
    my ($self, $filename, $revision, $content_array_ref) = @_;
62
 
63
    # Sanitise the filename.
64
    $filename = sanitise_url_component($filename);
65
 
66
    my $read_data;
67
    my $read_stdout_fh = new FileHandle;
68
    open($read_stdout_fh, '>', \$read_data);
69
    my @args = ();
70
    push @args, 'cat';
71
    push @args, '--non-interactive';
72
    push @args, '--no-auth-cache';
73
    push @args, @{ $self->{userCmdLine} };
74
    push @args, '--revision';
75
    push @args, $revision;
76
    push @args, $self->{repository_url} . '/' . $filename;
77
    Codestriker::execute_command($read_stdout_fh, undef,
78
				 $Codestriker::svn, @args);
79
 
80
    # Process the data for the topic.
81
    open($read_stdout_fh, '<', \$read_data);
82
    for (my $i = 1; <$read_stdout_fh>; $i++) {
83
	$_ = Codestriker::decode_topic_text($_);
84
	chop;
85
	$$content_array_ref[$i] = $_;
86
    }
87
}
88
 
89
# Retrieve the "root" of this repository.
90
sub getRoot ($) {
91
    my ($self) = @_;
92
    return $self->{repository_url};
93
}
94
 
95
# Return a URL which views the specified file and revision.
96
sub getViewUrl ($$$) {
97
    my ($self, $filename, $revision) = @_;
98
 
99
    # Lookup the file viewer from the configuration.
100
    my $viewer = $Codestriker::file_viewer->{$self->toString()};
101
    if (! (defined $viewer)) {
102
	$viewer = $Codestriker::file_viewer->{$self->{repository_string}};
103
    }
104
 
105
    return (defined $viewer) ? $viewer . "/" . $filename : "";
106
}
107
 
108
# Return a string representation of this repository.
109
sub toString ($) {
110
    my ($self) = @_;
111
    return "svn:" . $self->getRoot();
112
}
113
 
114
# Given a Subversion URL, determine if it refers to a directory or a file.
115
sub is_file_url {
116
    my ($self, $url) = @_;
117
    my $file_url;
118
 
119
    eval {
120
	my @args = ();
121
	push @args, 'info';
122
	push @args, '--non-interactive';
123
	push @args, '--no-auth-cache';
124
	push @args, @{ $self->{userCmdLine} };
125
	push @args, '--xml';
126
	push @args, $self->{repository_url} . '/' . $url;
127
	my $read_data;
128
	my $read_stdout_fh = new FileHandle;
129
	open($read_stdout_fh, '>', \$read_data);
130
 
131
	Codestriker::execute_command($read_stdout_fh, undef,
132
				     $Codestriker::svn, @args);
133
	open($read_stdout_fh, '<', \$read_data);
134
	while (<$read_stdout_fh>) {
135
	    if (/kind\s*\=\s*\"(\w+)\"/) {
136
		$file_url = $1 eq "File";
137
		last;
138
	    }
139
	}
140
    };
141
    if ($@ || !(defined $file_url)) {
142
	# The above command failed, try using the older method which only works
143
	# in an English locale.  This supports Subversion 1.2 or earlier
144
	# releases, which don't support the --xml flag for the info command.
145
	my @args = ();
146
	push @args, 'cat';
147
	push @args, '--non-interactive';
148
	push @args, '--no-auth-cache';
149
	push @args, @{ $self->{userCmdLine} };
150
	push @args, '--revision';
151
	push @args, 'HEAD';
152
	push @args, $self->{repository_url} . '/' . $url;
153
 
154
	my $read_stdout_data;
155
	my $read_stdout_fh = new FileHandle;
156
	open($read_stdout_fh, '>', \$read_stdout_data);
157
 
158
	my $read_stderr_data;
159
	my $read_stderr_fh = new FileHandle;
160
	open($read_stderr_fh, '>', \$read_stderr_data);
161
 
162
	Codestriker::execute_command($read_stdout_fh, $read_stderr_fh,
163
				     $Codestriker::svn, @args);
164
	$file_url = 1;
165
	open($read_stderr_fh, '<', \$read_stderr_data);
166
	while(<$read_stderr_fh>) {
167
	    if (/^svn:.* refers to a directory/) {
168
		$file_url = 0;
169
		last;
170
	    }
171
	}
172
    }
173
 
174
    return $file_url;
175
}
176
 
177
# The getDiff operation, pull out a change set based on the start and end 
178
# revision number, confined to the specified moduled_name.
179
sub getDiff {
180
    my ($self, $start_tag, $end_tag, $module_name, $stdout_fh, $stderr_fh) = @_;
181
 
182
    # Sanitise the URL, and determine if it refers to a directory or filename.
183
    $module_name = sanitise_url_component($module_name);
184
    my $directory;
185
    if ($self->is_file_url($module_name)) {
186
	$module_name =~ /(.*)\/[^\/]+/;
187
	$directory = $1;
188
    } else {
189
	$directory = $module_name;
190
    }
191
 
192
    # Execute the diff command.
193
    my $read_stdout_data;
194
    my $read_stdout_fh = new FileHandle;
195
    open($read_stdout_fh, '>', \$read_stdout_data);
196
 
197
    my @args = ();
198
    push @args, 'diff';
199
    push @args, '--non-interactive';
200
    push @args, '--no-auth-cache';
201
    push @args, @{ $self->{userCmdLine} };
202
    push @args, '-r';
203
    push @args, $start_tag . ':' . $end_tag;
204
    push @args, '--old';
205
    push @args, $self->{repository_url};
206
    push @args, $module_name;
207
    Codestriker::execute_command($read_stdout_fh, $stderr_fh,
208
				 $Codestriker::svn, @args);
209
 
210
    open($read_stdout_fh, '<', \$read_stdout_data);
211
    while(<$read_stdout_fh>) {
212
	my $line = $_;
213
 
214
	# If the user specifies a path (a branch in Subversion), the
215
	# diff file does not come back with a path rooted from the
216
	# repository base making it impossible to pull the entire file
217
	# back out. This code attempts to change the diff file on the
218
	# fly to ensure that the full path is present. This is a bug
219
	# against Subversion, so eventually it will be fixed, so this
220
	# code can't break when the diff command starts returning the
221
	# full path.
222
	if ($line =~ /^--- / || $line =~ /^\+\+\+ / ||
223
	    $line =~ /^Index: /) {
224
	    # Check if the bug has been fixed.
225
	    if ($line =~ /^\+\+\+ $module_name/ == 0 && 
226
		$line =~ /^--- $module_name/ == 0 &&
227
		$line =~ /^Index: $module_name/ == 0) {
228
		    $line =~ s/^--- /--- $directory\// or
229
		    $line =~ s/^Index: /Index: $directory\// or
230
		    $line =~ s/^\+\+\+ /\+\+\+ $directory\//;
231
	    }
232
	}
233
 
234
	print $stdout_fh $line;
235
    }
236
 
237
    return $Codestriker::OK;
238
}
239
 
240
1;