Subversion Repositories DevTools

Rev

Go to most recent revision | 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
    # Determine if there are additional parameters required for user
20
    # authentication.
21
    my @userCmdLine = ();
22
    if (defined($user) && defined($password)) {
23
        push @userCmdLine, '--username';
1299 dpurdie 24
        push @userCmdLine, $user;
25
        push @userCmdLine, '--password';
26
        push @userCmdLine, $password;
1293 dpurdie 27
    }
28
 
29
    # Sanitise the repository URL.
30
    $repository_url = sanitise_url_component($repository_url);
31
 
32
    my $self = {};
33
    $self->{repository_url} = $repository_url;
34
    $self->{userCmdLine} = \@userCmdLine;
35
    $self->{repository_string} = $repository_url;
36
    $self->{repository_string} .= ";$user" if defined $user;
37
    $self->{repository_string} .= ";$password" if defined $password;
38
    if ($self->{repository_string} !~ /^svn:/) {
1299 dpurdie 39
        $self->{repository_string} = "svn:" . $self->{repository_string};
1293 dpurdie 40
    }
41
 
42
    bless $self, $type;
43
}
44
 
45
# Sanitise a Subversion URL component, by replacing spaces with %20 and @
46
# symbols with %40, so that there is no confused with pegged revisions.  Also
47
# remove any leading and trailing slashes.
48
sub sanitise_url_component {
49
    my $url = shift;
50
    $url =~ s/\/$//;
51
    $url =~ s/^\///;
52
    $url =~ s/ /%20/g;
53
    $url =~ s/\@/%40/g;
54
    return $url;
55
}
56
 
57
# Retrieve the data corresponding to $filename and $revision.  Store each line
58
# into $content_array_ref.
59
sub retrieve ($$$\$) {
60
    my ($self, $filename, $revision, $content_array_ref) = @_;
61
 
62
    # Sanitise the filename.
63
    $filename = sanitise_url_component($filename);
64
 
65
    my $read_data;
66
    my $read_stdout_fh = new FileHandle;
67
    open($read_stdout_fh, '>', \$read_data);
68
    my @args = ();
69
    push @args, 'cat';
70
    push @args, '--non-interactive';
71
    push @args, '--no-auth-cache';
1299 dpurdie 72
    push @args, '--trust-server-cert';
1293 dpurdie 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,
1299 dpurdie 78
                                 $Codestriker::svn, @args);
1293 dpurdie 79
 
80
    # Process the data for the topic.
81
    open($read_stdout_fh, '<', \$read_data);
82
    for (my $i = 1; <$read_stdout_fh>; $i++) {
1299 dpurdie 83
        $_ = Codestriker::decode_topic_text($_);
84
        chop;
85
        $$content_array_ref[$i] = $_;
1293 dpurdie 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)) {
1299 dpurdie 102
        $viewer = $Codestriker::file_viewer->{$self->{repository_string}};
1293 dpurdie 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 {
1299 dpurdie 120
        my @args = ();
121
        push @args, 'info';
122
        push @args, '--non-interactive';
123
        push @args, '--no-auth-cache';
124
        push @args, '--trust-server-cert';
125
        push @args, @{ $self->{userCmdLine} };
126
        push @args, '--xml';
127
        push @args, $self->{repository_url} . '/' . $url;
128
        my $read_data;
129
        my $read_stdout_fh = new FileHandle;
130
        open($read_stdout_fh, '>', \$read_data);
1293 dpurdie 131
 
1299 dpurdie 132
        Codestriker::execute_command($read_stdout_fh, undef,
133
                                     $Codestriker::svn, @args);
134
        open($read_stdout_fh, '<', \$read_data);
135
        while (<$read_stdout_fh>) {
136
            if (/kind\s*\=\s*\"(\w+)\"/) {
137
                $file_url = $1 eq "File";
138
                last;
139
            }
140
        }
1293 dpurdie 141
    };
142
    if ($@ || !(defined $file_url)) {
1299 dpurdie 143
        # The above command failed, try using the older method which only works
144
        # in an English locale.  This supports Subversion 1.2 or earlier
145
        # releases, which don't support the --xml flag for the info command.
146
        my @args = ();
147
        push @args, 'cat';
148
        push @args, '--non-interactive';
149
        push @args, '--no-auth-cache';
150
        push @args, '--trust-server-cert';
151
        push @args, @{ $self->{userCmdLine} };
152
        push @args, '--revision';
153
        push @args, 'HEAD';
154
        push @args, $self->{repository_url} . '/' . $url;
1293 dpurdie 155
 
1299 dpurdie 156
        my $read_stdout_data;
157
        my $read_stdout_fh = new FileHandle;
158
        open($read_stdout_fh, '>', \$read_stdout_data);
1293 dpurdie 159
 
1299 dpurdie 160
        my $read_stderr_data;
161
        my $read_stderr_fh = new FileHandle;
162
        open($read_stderr_fh, '>', \$read_stderr_data);
1293 dpurdie 163
 
1299 dpurdie 164
        Codestriker::execute_command($read_stdout_fh, $read_stderr_fh,
165
                                     $Codestriker::svn, @args);
166
        $file_url = 1;
167
        open($read_stderr_fh, '<', \$read_stderr_data);
168
        while(<$read_stderr_fh>) {
169
            if (/^svn:.* refers to a directory/) {
170
                $file_url = 0;
171
                last;
172
            }
173
        }
1293 dpurdie 174
    }
175
 
176
    return $file_url;
177
}
178
 
179
# The getDiff operation, pull out a change set based on the start and end 
180
# revision number, confined to the specified moduled_name.
181
sub getDiff {
182
    my ($self, $start_tag, $end_tag, $module_name, $stdout_fh, $stderr_fh) = @_;
183
 
1300 dpurdie 184
 
185
    #
186
    #   Check Module Name
187
    #
188
    my @errors;
189
    $self->{getDiffError} = undef;
190
    unless ( $module_name =~ m~/(tags|branches|trunk)(/|$)~ )
191
    {
192
        push @errors,
193
            "Module does not contain 'tags', 'trunk' or 'branches'",
194
            "Module must specify a development branch or trunk";
195
    }
196
 
197
    if ( $module_name =~ m~^/~ )
198
    {
199
        push @errors, "Module must not start with a '/'"
200
    }
201
 
202
 
203
    if ( @errors )
204
    {
205
        $self->{getDiffError} = join ('<br>', @errors);
206
        return $Codestriker::OK;
207
    }
208
 
209
 
1293 dpurdie 210
    # Sanitise the URL, and determine if it refers to a directory or filename.
211
    $module_name = sanitise_url_component($module_name);
212
    my $directory;
213
    if ($self->is_file_url($module_name)) {
1299 dpurdie 214
        $module_name =~ /(.*)\/[^\/]+/;
215
        $directory = $1;
1293 dpurdie 216
    } else {
1299 dpurdie 217
        $directory = $module_name;
1293 dpurdie 218
    }
219
 
220
    # Execute the diff command.
221
    my $read_stdout_data;
222
    my $read_stdout_fh = new FileHandle;
223
    open($read_stdout_fh, '>', \$read_stdout_data);
224
 
1300 dpurdie 225
    #
226
    #   Determine the commad to use
227
    #   If user provides two numbers, then module is a full path
228
    #   Otherwise assume that the user has provided named tags
229
    #
230
    #
1293 dpurdie 231
    my @args = ();
232
    push @args, 'diff';
233
    push @args, '--non-interactive';
234
    push @args, '--no-auth-cache';
1299 dpurdie 235
    push @args, '--trust-server-cert';
1293 dpurdie 236
    push @args, @{ $self->{userCmdLine} };
1300 dpurdie 237
 
238
    if ( $start_tag =~ m~^\d+$~ )
239
    {
240
        my $rtag = $start_tag;
241
        $rtag .= (':' . $end_tag) if $end_tag;
242
        push @args, '-r';
243
        push @args, $rtag;
244
        push @args, '--old';
245
        push @args, $self->{repository_url};
246
        push @args, $module_name;
247
    }
248
    else
249
    {
250
        my $clean_module_name = $module_name;
251
        $clean_module_name =~ s~/trunk$~~;
252
        $clean_module_name =~ s~/branches/.*~~;
253
        $clean_module_name =~ s~/tags/.*~~;
254
        push @args, '--old';
255
        push @args, join ('/', $self->{repository_url}, $clean_module_name, 'tags', $start_tag);
256
 
257
        if ( $end_tag )
258
        {
259
            push @args, '--new';
260
            push @args, join ('/', $self->{repository_url}, $clean_module_name, 'tags', $end_tag);
261
        }
262
        else
263
        {
264
            push @args, '--new';
265
            push @args, join ('/', $self->{repository_url}, $module_name);
266
        }
267
    }
1293 dpurdie 268
    Codestriker::execute_command($read_stdout_fh, $stderr_fh,
1299 dpurdie 269
                                 $Codestriker::svn, @args);
1293 dpurdie 270
 
1300 dpurdie 271
    my $rv = open($read_stdout_fh, '<', \$read_stdout_data);
272
    if ( $rv ) {
273
        while(<$read_stdout_fh>) {
274
            my $line = $_;
1299 dpurdie 275
 
1300 dpurdie 276
            # If the user specifies a path (a branch in Subversion), the
277
            # diff file does not come back with a path rooted from the
278
            # repository base making it impossible to pull the entire file
279
            # back out. This code attempts to change the diff file on the
280
            # fly to ensure that the full path is present. This is a bug
281
            # against Subversion, so eventually it will be fixed, so this
282
            # code can't break when the diff command starts returning the
283
            # full path.
284
            if ($line =~ /^--- / || $line =~ /^\+\+\+ / ||
285
                $line =~ /^Index: /) {
286
                # Check if the bug has been fixed.
287
                if ($line =~ /^\+\+\+ $module_name/ == 0 && 
288
                    $line =~ /^--- $module_name/ == 0 &&
289
                    $line =~ /^Index: $module_name/ == 0) {
290
                        $line =~ s/^--- /--- $directory\// or
291
                        $line =~ s/^Index: /Index: $directory\// or
292
                        $line =~ s/^\+\+\+ /\+\+\+ $directory\//;
293
                }
1299 dpurdie 294
            }
1300 dpurdie 295
 
296
            print $stdout_fh $line;
1299 dpurdie 297
        }
1293 dpurdie 298
    }
299
 
300
    return $Codestriker::OK;
301
}
302
 
303
1;