File Coverage

blib/lib/App/GitHub/FixRepositoryName.pm
Criterion Covered Total %
statement 30 152 19.7
branch 0 76 0.0
condition 0 5 0.0
subroutine 10 18 55.5
pod 0 4 0.0
total 40 255 15.6


line stmt bran cond sub pod time code
1             package App::GitHub::FixRepositoryName;
2              
3 2     2   182173 use warnings;
  2         5  
  2         69  
4 2     2   11 use strict;
  2         4  
  2         128  
5              
6             =head1 NAME
7              
8             App::GitHub::FixRepositoryName - Fix your .git/config after a repository-name case change
9              
10             =head1 VERSION
11              
12             Version 0.02
13              
14             =cut
15              
16             our $VERSION = '0.02';
17              
18             =head1 SYNOPSIS
19              
20             github-fix-repository-name .git/config
21              
22             github-fix-repository-name My-Repository/ # ...should contain a .git directory
23              
24             cd .git; github-fix-repository
25              
26             # All of the above do the same thing, basically
27              
28             =head1 DESCRIPTION
29              
30             App::GitHub::FixRepositoryName will automatically find and update the github repository URLs in .git/config (so that they have
31             the right casing). It will first make a backup of your .git/config AND it will prompt you before writing out
32             the new config (and show it to you first)
33              
34             =head1 INSTALL
35              
36             You can install L by using L:
37              
38             cpan -i App::GitHub::FixRepositoryName
39              
40             If that doesn't work properly, you can find help at:
41              
42             http://sial.org/howto/perl/life-with-cpan/
43             http://sial.org/howto/perl/life-with-cpan/macosx/ # Help on Mac OS X
44             http://sial.org/howto/perl/life-with-cpan/non-root/ # Help with a non-root account
45              
46             =head1 CONTRIBUTE
47              
48             You can contribute or fork this project via GitHub:
49              
50             L
51              
52             git clone git://github.com/robertkrimen/App-GitHub-FixRepositoryName.git
53              
54             =cut
55              
56             =head1 USAGE
57              
58             =head2 github-fix-repository-name
59              
60             A commandline application that will fix a given .git/config to have the right repository name(s)
61              
62             Usage: github-fix-repository-name [...] ...
63              
64             --backup-to Backup 'config' to (default is the same directory)
65              
66             --no-backup Do not make a backup first
67              
68             --always-yes Assume yes when asking to write out the new config
69              
70             --help, -h, -? This help
71              
72             For example:
73              
74             github-fix-repository-name .git/config
75              
76             github-fix-repository-name My-Project1 xyzzy/My-Project2 # Fix many at once
77              
78             =head1 SEE ALSO
79              
80             L
81              
82             =cut
83              
84 2     2   2033 use File::AtomicWrite;
  2         72159  
  2         76  
85 2     2   2057 use App::GitHub::FindRepository;
  2         64708  
  2         86  
86 2     2   1994 use Path::Class;
  2         112259  
  2         156  
87 2     2   2813 use Carp::Clan;
  2         5022  
  2         17  
88 2     2   13510 use Term::Prompt qw/prompt/;
  2         48881  
  2         320  
89 2     2   7949 use Digest::SHA1 qw/sha1_hex/;
  2         2158  
  2         151  
90 2     2   14 use File::Temp qw/tempfile/;
  2         5  
  2         106  
91 2     2   38 use Getopt::Long;
  2         4  
  2         19  
92             $Term::Prompt::MULTILINE_INDENT = '';
93              
94             sub fix_file {
95 0     0 0   my $self = shift;
96 0           my $file = shift;
97            
98 0 0         croak "Wasn't given file to fix" unless defined $file;
99 0 0         croak "Can't read file \"$file\"" unless -r $file;
100            
101 0           $file = Path::Class::File->new( $file );
102              
103 0           my $original_content = $file->slurp;
104 0           my $content = $self->fix( $original_content );
105 0 0         return wantarray ? ($content, $original_content) : $content;
106             }
107              
108             sub fix {
109 0     0 0   my $self = shift;
110 0           my $content = shift;
111              
112 0 0         my $content_copy = ref $content eq 'SCALAR' ? $$content : $content;
113              
114             # TODO Better regexp
115 0           $content_copy =~ s!\b(git[\@:/]+github\.com[:/]\S+)!$self->_find_right_url( $1 )!ge;
  0            
116              
117 0           return $content_copy;
118             }
119              
120             sub _find_right_url {
121 0     0     my $self = shift;
122 0           my $url = shift;
123 0           my $repository;
124 0           eval {
125 0           $repository = App::GitHub::FindRepository->find( $url );
126             };
127 0 0         warn $@ if $@;
128 0 0         return $repository->url if $repository;
129 0           return $url; # Put back what we originally had
130             }
131              
132             sub do_usage(;$) {
133 0     0 0   my $error = shift;
134 0 0         warn $error if $error;
135 0           warn <<'_END_';
136              
137             Usage: github-fix-repository-name [...]
138              
139             --backup-to Backup 'config' to (default is the same directory)
140              
141             --no-backup Do not make a backup first
142              
143             --always-yes Assume yes when asking to write out the new config
144              
145             --help, -h, -? This help
146              
147             For example:
148              
149             github-fix-repository-name .git/config
150              
151             _END_
152              
153 0 0         exit -1 if $error;
154             }
155              
156             sub run {
157 0     0 0   my $self = shift;
158              
159 0           my ($backup_to, $no_backup, $always_yes, $help);
160 0           GetOptions(
161             'help|h|?' => \$help,
162             'backup-to=s' => \$backup_to,
163             'no-backup' => \$no_backup,
164             'always-yes|Y' => \$always_yes,
165             );
166              
167 0 0         if ($help) {
168 0           do_usage;
169 0           exit 0;
170             }
171              
172 0 0         my @fix = @ARGV ? @ARGV : qw/./;
173 0           for my $path (@fix) {
174 0           $self->_try_to_fix_file_or_directory( $path,
175             backup_to => $backup_to, no_backup => $no_backup, always_yes => $always_yes );
176             }
177             }
178              
179             sub _try_to_fix_file_or_directory {
180 0     0     my $self = shift;
181 0           my $path = shift;
182 0           my %given = @_;
183              
184 0           my $silent = $given{silent};
185 0 0   0     my $print = $silent ? sub {} : sub { print @_ };
  0            
  0            
186              
187 0           my $file;
188 0 0         if (-d $path ) {
    0          
189 0 0         if ( -d "$path/.git" ) { # The directory contains .git
  0 0          
190 0           $file = "$path/.git/config";
191             }
192             elsif ( 6 == grep { -e "$path/$_" } qw/branches config hooks info objects refs/ ) { # Looks like we're actually in .git
193 0           $file = "$path/config";
194             }
195             else {
196 0           croak "Don't know how to fix directory \"$path\"";
197             }
198             }
199             elsif (-f $path ) {
200 0           $file = $path;
201             }
202             else {
203 0           croak "Don't know how to fix path \"$path\"";
204             }
205              
206 0 0         croak "Can't read file \"$file\"" unless -r $file;
207 0 0         croak "Can't write file \"$file\"" unless -w _;
208              
209 0 0         if (! -s _ ) {
210 0           carp "File \"$file\" is empty";
211 0           return;
212             }
213              
214 0           my ($backup_file);
215 0           my ($content, $original_content) = $self->fix_file( $file );
216 0 0         if ($content eq $original_content) {
217 0           $print->( "Nothing to do to \"$file\"\n" );
218 0           return;
219             }
220             else {
221 0           $print->( $content );
222 0 0         $print->( "\n" ) unless $content =~ m/\n$/;
223 0           $print->( "---\n" );
224 0 0         unless ($given{always_yes}) {
225 0           my $Y = prompt( 'Y', "Do you want to write out the new .git/config to:\n\n$file\n\n? Y/n", 'Enter y or n', 'Y' );
226 0 0         unless ($Y) {
227 0           $print->( "Abandoning update to \"$file\"\n" );
228 0           return;
229             }
230             }
231 0 0         unless ( $given{no_backup} ) {
232 0           $backup_file = $self->_backup_file( $file, to => $given{backup_to}, template => $given{backup_template} );
233 0           $print->( "Made a backup of \"$file\" to \"$backup_file\"\n" );
234             }
235 0           File::AtomicWrite->write_file({ file => $file, input => \$content });
236 0           $print->( "Fixup of \"$file\" complete\n" );
237              
238 0           $file = Path::Class::File->new( "$file" );
239              
240 0 0         return wantarray ? ($file, $backup_file) : $file;
241             }
242             }
243              
244             # TODO: Factor this out to a CPAN module
245             sub _backup_file {
246 0     0     my $self = shift;
247 0           my $file = shift;
248 0           my %given = @_;
249              
250 0 0         croak "Wasn't given file to backup" unless defined $file;
251 0 0         croak "Can't read file \"$file\"" unless -r $file;
252              
253 0           $file = Path::Class::File->new( "$file" );
254              
255 0   0       my $to = $given{to} || $file->parent;
256              
257 0           $to = Path::Class::Dir->new( "$to" );
258              
259 0 0         $to->mkpath unless -e $to;
260              
261 0 0         croak "Backup destination \"$to\" is not a directory (or doesn't exist)" unless -d $to;
262 0 0         croak "Cannot write to backup destination \"$to\"" unless -w _;
263              
264 0   0       my $template = $given{template} || '.backup-%basename-%date-%tmp';
265              
266 0 0         if ($template =~ m/%fullpath\b/) {
267 0           my $value = $file.'';
268 0           $value =~ s!/+!-!g;
269 0           $template =~ s/%fullpath\b/$value/g;
270             }
271              
272 0 0         if ($template =~ m/%basename\b/) {
273 0           my $value = $file->basename;
274 0           $template =~ s/%basename\b/$value/g;
275             }
276              
277 0           my ($S, $M, $H, $d, $m, $Y) = localtime time;
278 0           $Y += 1900;
279              
280 0 0         if ($template =~ m/%date\b/) {
281 0           my $value = "$Y-$m-$d";
282 0           $template =~ s/%date\b/$value/g;
283             }
284              
285 0 0         if ($template =~ m/%time\b/) {
286 0           my $value = "$H:$M:$S";
287 0           $template =~ s/%time\b/$value/g;
288             }
289              
290 0           my ($tmp);
291              
292 0 0         if ($template =~ m/%tmp\b/) {
293 0           $tmp = 1;
294 0           my $value = "XXXXXX";
295 0           $template =~ s/%tmp\b/$value/g;
296             }
297              
298 0 0         if ($template =~ m/%sha1\b/) {
299 0           my $value = sha1_hex scalar $file->slurp;
300 0           $template =~ s/%sha1\b/$value/g;
301             }
302              
303 0           my ($handle, $backup_file);
304 0 0         if ($tmp) {
305 0           ($handle, $backup_file) = tempfile( $template, DIR => "$to", UNLINK => 0 );
306             }
307             else {
308 0           $backup_file = $to->file( $template );
309 0 0         $handle = $backup_file->openw or croak "Couldn't open \"$backup_file\": since $!";
310             }
311              
312 0           $handle->print( scalar $file->slurp );
313 0           close $handle;
314              
315 0           my $file_size = -s $file;
316 0           my $backup_file_size = -s $backup_file;
317              
318 0 0         croak "Couldn't backup \"$file\" ($file_size) to \"$backup_file\" ($backup_file_size): size doesn't match!" unless $file_size == $backup_file_size;
319              
320 0           return Path::Class::File->new( $backup_file );
321             }
322              
323             =head1 AUTHOR
324              
325             Robert Krimen, C<< >>
326              
327             =head1 BUGS
328              
329             Please report any bugs or feature requests to C, or through
330             the web interface at L. I will be notified, and then you'll
331             automatically be notified of progress on your bug as I make changes.
332              
333              
334              
335              
336             =head1 SUPPORT
337              
338             You can find documentation for this module with the perldoc command.
339              
340             perldoc App::GitHub::FixRepositoryName
341              
342              
343             You can also look for information at:
344              
345             =over 4
346              
347             =item * RT: CPAN's request tracker
348              
349             L
350              
351             =item * AnnoCPAN: Annotated CPAN documentation
352              
353             L
354              
355             =item * CPAN Ratings
356              
357             L
358              
359             =item * Search CPAN
360              
361             L
362              
363             =back
364              
365              
366             =head1 ACKNOWLEDGEMENTS
367              
368              
369             =head1 COPYRIGHT & LICENSE
370              
371             Copyright 2009 Robert Krimen, all rights reserved.
372              
373             This program is free software; you can redistribute it and/or modify it
374             under the same terms as Perl itself.
375              
376              
377             =cut
378              
379             __PACKAGE__; # End of App::GitHub::FixRepositoryName