📄 svn_load_dirs.pl.in
字号:
if ($line !~ /^F$/i) { print "Renaming $del_filename to $add_filename.\n"; $repeat_loop = 1; # Because subversion cannot rename the same file # or directory twice, which includes doing a # rename of a file in a directory that was # previously renamed, a commit has to be # performed. Check if the file or directory being # renamed now would cause such a problem and # commit if so. my $do_commit_now = 0; foreach my $rename_to_filename (keys %rename_to_files) { if (contained_in($del_filename, $rename_to_filename, $rename_to_files{$rename_to_filename}{type})) { $do_commit_now = 1; last; } } if ($do_commit_now) { print "Now committing previously run renames.\n"; &commit_renames($load_dir, \@renamed_filenames, \%rename_from_files, \%rename_to_files); } push(@renamed_filenames, $del_filename, $add_filename); { my $d = $del_files{$del_filename}; $rename_from_files{$del_filename} = $d; $rename_to_files{$add_filename} = $d; } # Check that any required directories to do the # rename exist. my @add_segments = split('/', $add_filename); pop(@add_segments); my $add_dir = ''; my @add_dirs; foreach my $segment (@add_segments) { $add_dir = length($add_dir) ? "$add_dir/$segment" : $segment; unless (-d $add_dir) { push(@add_dirs, $add_dir); } } if (@add_dirs) { read_from_process($svn, 'mkdir', @add_dirs); } read_from_process($svn, 'mv', $del_filename, $add_filename); } } } while ($repeat_loop); } # If there are any renames that have not been committed, then do # that now. if (@renamed_filenames) { &commit_renames($load_dir, \@renamed_filenames, \%rename_from_files, \%rename_to_files); } # At this point all renames have been performed. Now get the # final list of files and directories in the working copy # directory. The %add_files hash will contain the list of files # and directories to add to the working copy and %del_files starts # with all the files already in the working copy and gets files # removed that are in the imported directory, which results in a # list of files that should be deleted. %upd_files holds the list # of files that have been updated. my %add_files; my %del_files = &recursive_ls_and_hash($wc_import_dir_cwd); my %upd_files; # This anonymous subroutine copies files from the source directory # to the working copy directory. my $wanted = sub { s#^\./##; return if $_ eq '.'; my $source_path = $_; my $dest_path = "$wc_import_dir_cwd/$_"; my ($source_type, $source_is_exe) = &file_info($source_path); my ($dest_type) = &file_info($dest_path); return if ($source_type ne 'd' and $source_type ne 'f' and $source_type ne 'l'); # Fail if the destination type exists but is of a different # type of file than the source type. if ($dest_type ne '0' and $source_type ne $dest_type) { die "$0: does not handle changing source and destination type ", "for '$source_path'.\n"; } # Determine if the file is being added or is an update to an # already existing file using the file's digest. my $del_info = delete $del_files{$source_path}; if (defined $del_info) { if (defined (my $del_digest = $del_info->{digest})) { my $new_digest = &digest_hash_file($source_path); if ($new_digest ne $del_digest) { print "U $source_path\n"; $upd_files{$source_path} = $del_info; } } } else { print "A $source_path\n"; $add_files{$source_path}{type} = $source_type; # Create an array reference to hold the list of properties # to apply to this object. unless (defined $add_files{$source_path}{properties}) { $add_files{$source_path}{properties} = []; } # Go through the list of properties for a match on this # file or directory and if there is a match, then apply # the property to it. foreach my $property (@property_settings) { my $re = $property->{re}; if ($source_path =~ $re) { my $property_name = $property->{name}; my $property_value = $property->{value}; # The property value may not be set in the # configuration file, since the user may just want # to set the control flag. if (defined $property_name and defined $property_value) { # Ignore properties that do not apply to # directories. if ($source_type eq 'd') { if ($property_name eq 'svn:eol-style' or $property_name eq 'svn:executable' or $property_name eq 'svn:keywords' or $property_name eq 'svn:mime-type') { next; } } # Ignore properties that do not apply to # files. if ($source_type eq 'f') { if ($property_name eq 'svn:externals' or $property_name eq 'svn:ignore') { next; } } print "Adding to '$source_path' property ", "'$property_name' with value ", "'$property_value'.\n"; push(@{$add_files{$source_path}{properties}}, $property); } last if $property->{control} eq 'break'; } } } # Add svn:executable to files that have their executable bit # set. if ($source_is_exe) { print "Adding to '$source_path' property 'svn:executable' with ", "value '*'.\n"; my $property = {name => 'svn:executable', value => '*'}; push (@{$add_files{$source_path}{properties}}, $property); } # Now make sure the file or directory in the source directory # exists in the repository. if ($source_type eq 'd') { if ($dest_type eq '0') { mkdir($dest_path) or die "$0: cannot mkdir '$dest_path': $!\n"; } } elsif ($source_type eq 'l') { my $link_target = readlink($source_path) or die "$0: cannot readlink '$source_path': $!\n"; if ($dest_type eq 'l') { my $old_target = readlink($dest_path) or die "$0: cannot readlink '$dest_path': $!\n"; return if ($old_target eq $link_target); unlink($dest_path) or die "$0: unlink '$dest_path' failed: $!\n"; } symlink($link_target, $dest_path) or die "$0: cannot symlink '$dest_path' to '$link_target': $!\n"; } elsif ($source_type eq 'f') { # Only copy the file if the digests do not match. if ($add_files{$source_path} or $upd_files{$source_path}) { copy($source_path, $dest_path) or die "$0: copy '$source_path' to '$dest_path': $!\n"; } } else { die "$0: does not handle copying files of type '$source_type'.\n"; } }; # Now change into the directory containing the files to load. # First change to the original directory where this script was run # so that if the specified directory is a relative directory path, # then the script can change into it. chdir($orig_cwd) or die "$0: cannot chdir '$orig_cwd': $!\n"; chdir($load_dir) or die "$0: cannot chdir '$load_dir': $!\n"; find({no_chdir => 1, preprocess => sub { sort { $b cmp $a } grep { $_ !~ /^[._]svn$/ } @_ }, wanted => $wanted }, '.'); # The files and directories that are in %del_files are the files # and directories that need to be deleted. Because svn will # return an error if a file or directory is deleted in a directory # that subsequently is deleted, first find all directories and # remove from the list any files and directories inside those # directories from this list. Work through the list repeatedly # working from short to long names so that directories containing # other files and directories will be deleted first. my $repeat_loop; do { $repeat_loop = 0; my @del_files = sort {length($a) <=> length($b) || $a cmp $b} keys %del_files; &filter_renamed_files(\@del_files, \%rename_from_files); foreach my $file (@del_files) { if ($del_files{$file}{type} eq 'd') { my $dir = "$file/"; my $dir_length = length($dir); foreach my $f (@del_files) { next if $file eq $f; if (length($f) >= $dir_length and substr($f, 0, $dir_length) eq $dir) { print "d $f\n"; delete $del_files{$f}; $repeat_loop = 1; } } # If there were any deletions of files and/or # directories inside a directory that will be deleted, # then restart the entire loop again, because one or # more keys have been deleted from %del_files. # Equally important is not to stop this loop if no # deletions have been done, otherwise later # directories that may contain files and directories # to be deleted will not be deleted. last if $repeat_loop; } } } while ($repeat_loop); # What is left are files that are not in any directories to be # deleted and directories to be deleted. To delete the files, # deeper files and directories must be deleted first. Because we # have a hash keyed by remaining files and directories to be # deleted, instead of trying to figure out which directories and # files are contained in other directories, just reverse sort by # the path length and then alphabetically. my @del_files = sort {length($b) <=> length($a) || $a cmp $b } keys %del_files; &filter_renamed_files(\@del_files, \%rename_from_files); foreach my $file (@del_files) { print "D $file\n"; } # Now change back to the trunk directory and run the svn commands. chdir($wc_import_dir_cwd) or die "$0: cannot chdir '$wc_import_dir_cwd': $!\n"; # If any of the added files have the svn:eol-style property set, # then pass -b to diff, otherwise diff may fail because the end of # lines have changed and the source file and file in the # repository will not be identical. my @diff_ignore_space_changes; if (keys %add_files) { my @add_files = sort {length($a) <=> length($b) || $a cmp $b} keys %add_files; my $target_filename = &make_targets_file(@add_files); read_from_process($svn, 'add', '-N', '--targets', $target_filename); unlink($target_filename); # Add properties on the added files. foreach my $add_file (@add_files) { foreach my $property (@{$add_files{$add_file}{properties}}) { my $property_name = $property->{name}; my $property_value = $property->{value}; if ($property_name eq 'svn:eol-style') { @diff_ignore_space_changes = ('-b'); } # Write the value to a temporary file in case it's multi-line my ($handle, $tmpfile) = tempfile(DIR => $temp_dir); print $handle $property_value; close($handle); read_from_process($svn, 'propset', $property_name, '--file', $tmpfile, $add_file); } } } if (@del_files) { my $target_filename = &make_targets_file(@del_files); read_from_process($svn, 'rm', '--targets', $target_filename); unlink($target_filename); } # Go through the list of updated files and check the svn:eol-style # property. If it is set to native, then convert all CR, CRLF and # LF's in the file to the native end of line characters. Also, # modify diff's command line so that it will ignore the change in # end of line style. if (keys %upd_files) { my @upd_files = sort {length($a) <=> length($b) || $a cmp $b} keys %upd_files; foreach my $upd_file (@upd_files) { # Always append @BASE to a filename in case they contain a # @ character, in which case the Subversion command line # client will attempt to parse the characters after the @ # as a revision and most likely fail, or if the characters # after the @ are a valid revision, then it'll possibly # get the incorrect information. So always append @BASE # and any preceding @'s will be treated normally and the # correct information will be retrieved. my @command = ($svn, 'propget', 'svn:eol-style', "$upd_file\@BASE");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -