line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package SVN::Rami;
|
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
219694
|
use 5.10.1;
|
|
2
|
|
|
|
|
19
|
|
4
|
2
|
|
|
2
|
|
11
|
use strict;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
40
|
|
5
|
2
|
|
|
2
|
|
7
|
use warnings;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
57
|
|
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
10
|
use File::Basename;
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
247
|
|
8
|
2
|
|
|
2
|
|
27
|
use File::Path qw(make_path remove_tree);
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
3521
|
|
9
|
|
|
|
|
|
|
#use File::Spec; # Will use later for Windows file names.
|
10
|
|
|
|
|
|
|
#use Path::Class; # Will use later for Windows file names.
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# TODO: clearly define the words "version", "revision", "branch". (Maybe "version" isn't the right word to use.)
|
13
|
|
|
|
|
|
|
# TODO: sanitize version names: they should contain only \w and hyphen.
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
SVN::Rami - Automates merging to multiple branches
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '0.205';
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Used by the script L. This is not (yet) a stand-alone module.
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 SUBROUTINES
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
This module is only intended to be used by the script
|
32
|
|
|
|
|
|
|
L.
|
33
|
|
|
|
|
|
|
There are a few publicly available subroutines,
|
34
|
|
|
|
|
|
|
but they are not guaranteed to be compatible between revisions.
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=cut
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
#
|
39
|
|
|
|
|
|
|
# Documentation of public subroutines is provided inline below.
|
40
|
|
|
|
|
|
|
#
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
#
|
44
|
|
|
|
|
|
|
#Reads a two-column CSV file and converts it to key-value pairs.
|
45
|
|
|
|
|
|
|
#For example, if the file contains one line consisting of "a,b",
|
46
|
|
|
|
|
|
|
#this method returns a mapping of "a" to "b".
|
47
|
|
|
|
|
|
|
#The file is assumed to have no headers.
|
48
|
|
|
|
|
|
|
#
|
49
|
|
|
|
|
|
|
#BUG: the file must be formatted precisely right: even an empty
|
50
|
|
|
|
|
|
|
#line in the middle of the file will result in an incorrect mapping.
|
51
|
|
|
|
|
|
|
#
|
52
|
|
|
|
|
|
|
#TODO: use Text::CSV instead.
|
53
|
|
|
|
|
|
|
#
|
54
|
|
|
|
|
|
|
sub load_csv_as_map {
|
55
|
0
|
|
|
0
|
0
|
0
|
my $filename = shift;
|
56
|
0
|
|
|
|
|
0
|
local $/ = undef; # Slurp mode
|
57
|
0
|
0
|
|
|
|
0
|
open(my $handle, '<', $filename) or die "Could not read $filename\n";
|
58
|
0
|
|
|
|
|
0
|
$_ = <$handle>;
|
59
|
0
|
|
|
|
|
0
|
close $handle;
|
60
|
0
|
|
|
|
|
0
|
my @contents = split( /,|\R/ );
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Hack: the first two items are column headings.
|
63
|
0
|
|
|
|
|
0
|
(undef, undef, my @result) = @contents;
|
64
|
0
|
|
|
|
|
0
|
return @result;
|
65
|
|
|
|
|
|
|
}
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
#
|
69
|
|
|
|
|
|
|
# This utility function simply dumps data to a file.
|
70
|
|
|
|
|
|
|
# Example: write_file('foo.txt', 'Hello world') creates a file
|
71
|
|
|
|
|
|
|
# named foo.txt whose complete contents are "Hello world".
|
72
|
|
|
|
|
|
|
# Note that you can pass a multi-line string as the second argument.
|
73
|
|
|
|
|
|
|
#
|
74
|
|
|
|
|
|
|
sub write_file {
|
75
|
0
|
|
|
0
|
0
|
0
|
my $filename = shift;
|
76
|
0
|
|
|
|
|
0
|
my $contents = shift;
|
77
|
|
|
|
|
|
|
|
78
|
0
|
0
|
|
|
|
0
|
open(my $handle, '>', $filename) or die "Could not write to $filename\n";
|
79
|
0
|
|
|
|
|
0
|
print $handle $contents;
|
80
|
0
|
|
|
|
|
0
|
close $handle;
|
81
|
|
|
|
|
|
|
}
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
#
|
84
|
|
|
|
|
|
|
# Usage: find_revision($revision, %branch_to_url)
|
85
|
|
|
|
|
|
|
# Queries SVN for information about the revision.
|
86
|
|
|
|
|
|
|
# Returns a hash that maps 'branch' to the branch on which the revision
|
87
|
|
|
|
|
|
|
# was committed (e.g., 6.2) and that maps 'commit_message' to the
|
88
|
|
|
|
|
|
|
# comment the user wrote to explain the commit.
|
89
|
|
|
|
|
|
|
#
|
90
|
|
|
|
|
|
|
sub find_revision {
|
91
|
0
|
|
|
0
|
0
|
0
|
my $rev = shift;
|
92
|
0
|
|
|
|
|
0
|
my %branch_to_url = @_;
|
93
|
0
|
|
|
|
|
0
|
foreach my $branch (keys %branch_to_url) {
|
94
|
0
|
|
|
|
|
0
|
my $url = $branch_to_url{$branch};
|
95
|
0
|
|
|
|
|
0
|
$_ = `svn log -c $rev $url`;
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# If the revision was NOT on $branch, then the output
|
98
|
|
|
|
|
|
|
# will be a single line of dashes: ----------
|
99
|
|
|
|
|
|
|
#
|
100
|
|
|
|
|
|
|
# But if the revision WAS on $branch, then
|
101
|
|
|
|
|
|
|
# the output will be something like this:
|
102
|
|
|
|
|
|
|
# ------------------------------------------------------------------------
|
103
|
|
|
|
|
|
|
# r84187 | bob.smith | 2023-03-27 10:53:40 -0400 (Mon, 27 Mar 2023) | 4 lines
|
104
|
|
|
|
|
|
|
#
|
105
|
|
|
|
|
|
|
# Fixed memory leak
|
106
|
|
|
|
|
|
|
# ------------------------------------------------------------------------
|
107
|
0
|
0
|
|
|
|
0
|
if ( m/^-----+\R* # match a bunch of dashes followed by a newline.
|
108
|
|
|
|
|
|
|
^r.*?lines?\R+ # match line: r84187 | badelman | ... | 4 lines
|
109
|
|
|
|
|
|
|
^(.*?)\R+ # Match the commit message.
|
110
|
|
|
|
|
|
|
^-------+\R*\Z # Match a bunch of dashes followed by a newline.
|
111
|
|
|
|
|
|
|
/msx ) {
|
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
#if ( m/^-+\r?\n?.*?lines(\r?\n)+(.*?)\r?\n-+(\r?\n)*$/sx ) {
|
114
|
0
|
|
|
|
|
0
|
return ('branch'=>$branch, 'commit_message'=>$1 );
|
115
|
|
|
|
|
|
|
}
|
116
|
|
|
|
|
|
|
}
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Revision not found. Return an empty hash.
|
119
|
0
|
|
|
|
|
0
|
return ();
|
120
|
|
|
|
|
|
|
}
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# HACK: define these as essentially global for now.
|
124
|
|
|
|
|
|
|
our $repo = 'default'; # TODO: support more than one repo.
|
125
|
|
|
|
|
|
|
our $rami_home_dir = glob("~/.rami/repo/$repo"); # TODO: use File::HomeDir instead of ~/
|
126
|
|
|
|
|
|
|
our $conf_dir = "$rami_home_dir/conf";
|
127
|
|
|
|
|
|
|
our $temp_dir = "$rami_home_dir/temp";
|
128
|
|
|
|
|
|
|
our $commit_message_file = "$temp_dir/message.txt";
|
129
|
|
|
|
|
|
|
our $path_csv_file = "$conf_dir/paths.csv"; # Not currently used.
|
130
|
|
|
|
|
|
|
our $root_work_dir = "$rami_home_dir/work"; # Holds a subdirectory for each branch.
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
#=head2 rami_main
|
134
|
|
|
|
|
|
|
#
|
135
|
|
|
|
|
|
|
#The main module. Currently takes one argument,
|
136
|
|
|
|
|
|
|
#which is the SVN revision which shoulde be merged.
|
137
|
|
|
|
|
|
|
#
|
138
|
|
|
|
|
|
|
#=cut
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub rami_main {
|
141
|
0
|
|
|
0
|
0
|
0
|
my $source_revision = shift;
|
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
my $conf_dir = $SVN::Rami::conf_dir;
|
144
|
0
|
|
|
|
|
0
|
my $commit_message_file = $SVN::Rami::commit_message_file;
|
145
|
0
|
|
|
|
|
0
|
my $path_csv_file = $SVN::Rami::path_csv_file;
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#print "### $commit_message_file\n";
|
148
|
|
|
|
|
|
|
|
149
|
0
|
0
|
|
|
|
0
|
die "Expected directory $conf_dir\n" unless -d $conf_dir;
|
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# my %branch_to_path_on_filesystem = load_csv_as_map($path_csv_file);
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# We need the list of branches to be in order.
|
154
|
0
|
|
|
|
|
0
|
my @branch_to_url_array = load_csv_as_map("$conf_dir/urls.csv");
|
155
|
0
|
|
|
|
|
0
|
my %branch_to_url = @branch_to_url_array;
|
156
|
0
|
|
|
|
|
0
|
my @branches = grep( !/^http/, @branch_to_url_array); # HACK: remove URLs, leaving only versions, IN ORDER!
|
157
|
|
|
|
|
|
|
|
158
|
0
|
|
|
|
|
0
|
my %branch_to_path_on_filesystem = map { $_ => convert_branches_to_filesystem_paths($_) } @branches;
|
|
0
|
|
|
|
|
0
|
|
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
my %revision_details = find_revision($source_revision, %branch_to_url);
|
161
|
0
|
0
|
|
|
|
0
|
if ( ! %revision_details ) {
|
162
|
0
|
|
|
|
|
0
|
die "Unable to find revision $source_revision\n";
|
163
|
|
|
|
|
|
|
}
|
164
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
0
|
my $source_branch = $revision_details{'branch'};
|
166
|
|
|
|
|
|
|
# The revision comment. We will later append something like "merge r 123 from 2.2.2"
|
167
|
0
|
|
|
|
|
0
|
my $base_commit_message = $revision_details{'commit_message'};
|
168
|
0
|
|
|
|
|
0
|
$base_commit_message =~ s/(\r\n\R\s)+$//g; # Remove trailing whitespace/newlines.
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
|
171
|
0
|
|
|
|
|
0
|
my $found_branch = 0; # Will be true when we loop to the source branch.
|
172
|
|
|
|
|
|
|
#my $previous_branch = '';
|
173
|
0
|
|
|
|
|
0
|
foreach my $target_branch (@branches) {
|
174
|
0
|
0
|
|
|
|
0
|
if ($target_branch eq $source_branch) {
|
|
|
0
|
|
|
|
|
|
175
|
0
|
|
|
|
|
0
|
$found_branch = 1;
|
176
|
|
|
|
|
|
|
# Perhaps instead of if-else we could use "redo", which is similar to Java's "continue"
|
177
|
|
|
|
|
|
|
} elsif ( $found_branch ) { # If we found the branch last time...
|
178
|
0
|
|
|
|
|
0
|
print "------------------------ Merging r $source_revision from $source_branch to $target_branch\n";
|
179
|
0
|
|
|
|
|
0
|
my $source_url = $branch_to_url{$source_branch};
|
180
|
0
|
|
|
|
|
0
|
my $working_dir = $branch_to_path_on_filesystem{$target_branch};
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# Get our working directory to the correct revision.
|
183
|
0
|
|
|
|
|
0
|
chdir($working_dir);
|
184
|
0
|
|
|
|
|
0
|
system("svn revert -R ."); # or die "Failed to revert $working_dir";
|
185
|
0
|
|
|
|
|
0
|
system("svn up"); # or die "Failed to update $working_dir";
|
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# Write the commit message to a file.
|
188
|
0
|
|
|
|
|
0
|
my $commit_message = "$base_commit_message (merge r $source_revision from $source_branch)";
|
189
|
0
|
|
|
|
|
0
|
write_file($commit_message_file, $commit_message);
|
190
|
|
|
|
|
|
|
|
191
|
0
|
|
|
|
|
0
|
my $merge_command = "svn merge --accept postpone -c $source_revision $source_url .";
|
192
|
0
|
|
|
|
|
0
|
print "$merge_command\n";
|
193
|
0
|
|
|
|
|
0
|
my $output_from_merge = `$merge_command`;
|
194
|
0
|
|
|
|
|
0
|
print "$output_from_merge\n";
|
195
|
0
|
0
|
|
|
|
0
|
if ($output_from_merge =~ /Summary of conflicts/) { # If there were merge conflicts
|
196
|
0
|
|
|
|
|
0
|
print "Failed to merge r $source_revision from $source_branch to $target_branch.\n";
|
197
|
0
|
|
|
|
|
0
|
print "Merge conflicts in $working_dir\n";
|
198
|
0
|
|
|
|
|
0
|
die;
|
199
|
|
|
|
|
|
|
}
|
200
|
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
0
|
my $commit_command = "svn commit --file $commit_message_file";
|
202
|
0
|
|
|
|
|
0
|
print "$commit_command\n";
|
203
|
0
|
|
|
|
|
0
|
my $output_from_commit = `$commit_command`;
|
204
|
0
|
|
|
|
|
0
|
my $target_revision;
|
205
|
0
|
|
|
|
|
0
|
print "$output_from_commit\n";
|
206
|
0
|
0
|
|
|
|
0
|
if ($output_from_commit =~ /Committed revision (\d+)\./) {
|
207
|
0
|
|
|
|
|
0
|
$target_revision = $1;
|
208
|
|
|
|
|
|
|
} else {
|
209
|
0
|
|
|
|
|
0
|
die "Failed to commit r $source_revision from $source_branch to $target_branch.\n";
|
210
|
|
|
|
|
|
|
}
|
211
|
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
0
|
$source_branch = $target_branch;
|
213
|
0
|
|
|
|
|
0
|
$source_revision = $target_revision;
|
214
|
|
|
|
|
|
|
} else {
|
215
|
|
|
|
|
|
|
# print "Skipping branch $target_branch because we don't need to merge it.\n";
|
216
|
|
|
|
|
|
|
}
|
217
|
|
|
|
|
|
|
}
|
218
|
|
|
|
|
|
|
|
219
|
0
|
0
|
|
|
|
0
|
if ( ! $found_branch ) {
|
220
|
0
|
|
|
|
|
0
|
die "Unrecognized branch $source_branch\n";
|
221
|
|
|
|
|
|
|
}
|
222
|
|
|
|
|
|
|
}
|
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head2 windows_safe_remove_tree
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
windows_safe_remove_tree "foo/bar", "c:\\temp"
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
B Removes a directory and all its sub-directories.
|
230
|
|
|
|
|
|
|
Same as L,
|
231
|
|
|
|
|
|
|
but provides a work-around for a Windows issue involving very long filenames.
|
232
|
|
|
|
|
|
|
Works on all operating systems, not just Windows.
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=head3 Statement of the problem
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
On Windows it is possible to create files that have more than
|
238
|
|
|
|
|
|
|
L=260
|
239
|
|
|
|
|
|
|
characters, but Windows will
|
240
|
|
|
|
|
|
|
L.
|
241
|
|
|
|
|
|
|
If you try to delete them manually, you might see this error message:
|
242
|
|
|
|
|
|
|
"The source file names are larger than is supported by the file system".
|
243
|
|
|
|
|
|
|
If you try to delete the files with C,
|
244
|
|
|
|
|
|
|
you might see this error message:
|
245
|
|
|
|
|
|
|
"cannot remove directory for [...]: Directory not empty".
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head3 Existing work-arounds
|
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
Besides this function, many work-arounds have been proposed. See:
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=over
|
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=item * A L which uses L.
|
254
|
|
|
|
|
|
|
(Note that the maintainers of L
|
255
|
|
|
|
|
|
|
have L.)
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=item * L
|
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item * L
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=back
|
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=cut
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub windows_safe_remove_tree {
|
266
|
0
|
0
|
|
0
|
1
|
0
|
if ($^O ne 'MSWin32') {
|
267
|
|
|
|
|
|
|
# Normal case: not Windows
|
268
|
0
|
|
|
|
|
0
|
File::Path::remove_tree @_;
|
269
|
|
|
|
|
|
|
} else {
|
270
|
|
|
|
|
|
|
# Windows: do the work-around
|
271
|
0
|
|
|
|
|
0
|
my @filenames = @_;
|
272
|
0
|
|
|
|
|
0
|
for (@filenames) { s#/#\\#g } # Convert slashes to backslashes, e.g., a/b/c -> a\b\c
|
|
0
|
|
|
|
|
0
|
|
273
|
0
|
|
|
|
|
0
|
my $windows_filenames = join(' ', @filenames);
|
274
|
0
|
|
|
|
|
0
|
exec("rmdir /Q /S $windows_filenames");
|
275
|
|
|
|
|
|
|
}
|
276
|
|
|
|
|
|
|
}
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
#=head2 load_config_from_SVN_url
|
279
|
|
|
|
|
|
|
#
|
280
|
|
|
|
|
|
|
#Secondary function: initializes based on a configuration
|
281
|
|
|
|
|
|
|
#file which is loaded from SVN.
|
282
|
|
|
|
|
|
|
#Takes one argument, which is the name of the config file.
|
283
|
|
|
|
|
|
|
#
|
284
|
|
|
|
|
|
|
#=cut
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
sub load_config_from_SVN_url {
|
287
|
0
|
|
|
0
|
0
|
0
|
my $svn_url = shift; # URL of our config file in SVN.
|
288
|
0
|
|
|
|
|
0
|
my $rami_home_dir = $SVN::Rami::rami_home_dir;
|
289
|
0
|
|
|
|
|
0
|
my $conf_dir = $SVN::Rami::conf_dir;
|
290
|
0
|
|
|
|
|
0
|
my $temp_dir = $SVN::Rami::temp_dir;
|
291
|
0
|
|
|
|
|
0
|
my $root_work_dir = $SVN::Rami::root_work_dir;
|
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# TODO: don't wipe out the old configuration until we know that the new configuration exists.
|
294
|
|
|
|
|
|
|
|
295
|
0
|
0
|
|
|
|
0
|
remove_tree $rami_home_dir if -d $rami_home_dir;
|
296
|
0
|
|
|
|
|
0
|
make_path $conf_dir, $temp_dir, $root_work_dir;
|
297
|
|
|
|
|
|
|
|
298
|
0
|
|
|
|
|
0
|
my $result_of_export = EXEC("svn export --force $svn_url $conf_dir");
|
299
|
0
|
0
|
|
|
|
0
|
die "Failed to load configuration from SVN\n" unless ($result_of_export =~ m/Export complete|Exported revision/);
|
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Load the branches.
|
302
|
|
|
|
|
|
|
# This code is COPY-AND-PASTED from rami_main().
|
303
|
0
|
|
|
|
|
0
|
my @branch_to_url_array = load_csv_as_map("$conf_dir/urls.csv");
|
304
|
0
|
|
|
|
|
0
|
my %branch_to_url = @branch_to_url_array;
|
305
|
0
|
|
|
|
|
0
|
my @branches = grep( !/^http/, @branch_to_url_array); # HACK: remove URLs, leaving only versions, IN ORDER!
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Checkout the branches onto the local filesystem.
|
308
|
0
|
|
|
|
|
0
|
while (my($branch,$url) = each(%branch_to_url)) {
|
309
|
0
|
|
|
|
|
0
|
my $filesystem_path = convert_branches_to_filesystem_paths($branch);
|
310
|
0
|
|
|
|
|
0
|
my $command = "svn checkout $url $filesystem_path";
|
311
|
0
|
|
|
|
|
0
|
print "$command\n";
|
312
|
0
|
|
|
|
|
0
|
system($command);
|
313
|
|
|
|
|
|
|
}
|
314
|
|
|
|
|
|
|
}
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
#
|
317
|
|
|
|
|
|
|
# Converts a list of logical branch names to the place on the filesystem where they should be.
|
318
|
|
|
|
|
|
|
#
|
319
|
|
|
|
|
|
|
# Example: convert_branches_to_filesystem_paths('trunk')
|
320
|
|
|
|
|
|
|
# might return '/home/bob/.rami/repo/default/work/bob'
|
321
|
|
|
|
|
|
|
#
|
322
|
|
|
|
|
|
|
# In list context, returns a list.
|
323
|
|
|
|
|
|
|
# In scalar context, returns the first item of the list.
|
324
|
|
|
|
|
|
|
#
|
325
|
|
|
|
|
|
|
sub convert_branches_to_filesystem_paths {
|
326
|
2
|
|
|
2
|
0
|
140
|
my $rami_home_dir = $SVN::Rami::rami_home_dir;
|
327
|
2
|
|
|
|
|
8
|
my @result = map { "$rami_home_dir/work/$_" } @_;
|
|
3
|
|
|
|
|
12
|
|
328
|
2
|
100
|
|
|
|
17
|
return wantarray ? @result : $result[0];
|
329
|
|
|
|
|
|
|
}
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
#
|
332
|
|
|
|
|
|
|
# Executes on the command line and returns the output.
|
333
|
|
|
|
|
|
|
# Example: my $list = EXEC('ls');
|
334
|
|
|
|
|
|
|
#
|
335
|
|
|
|
|
|
|
# This is just a wrapper for back-tick, i.e.,
|
336
|
|
|
|
|
|
|
# my $result = `$command`;
|
337
|
|
|
|
|
|
|
# but it might later allow us to unit-test.
|
338
|
|
|
|
|
|
|
#
|
339
|
|
|
|
|
|
|
sub EXEC {
|
340
|
0
|
|
|
0
|
0
|
|
my $command = shift;
|
341
|
|
|
|
|
|
|
# my %args = ...
|
342
|
0
|
|
|
|
|
|
print "$command\n";
|
343
|
0
|
|
|
|
|
|
my $result = `$command`;
|
344
|
0
|
|
|
|
|
|
print "$result\n";
|
345
|
0
|
|
|
|
|
|
return $result;
|
346
|
|
|
|
|
|
|
}
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=head1 AUTHOR
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Dan Richter, C<< >>
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=head1 BUGS
|
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through
|
355
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll
|
356
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes.
|
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head1 SUPPORT
|
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command.
|
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
perldoc SVN::Rami
|
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
You can also look for information at:
|
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=over 4
|
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here)
|
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
L
|
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=item * CPAN Ratings
|
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
L
|
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=item * Search CPAN
|
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
L
|
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=back
|
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=head1 SEE ALSO
|
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
L
|
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
This software is copyright (c) 2023 by Dan Richter.
|
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under
|
395
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself.
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=cut
|
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
1; # End of SVN::Rami
|