1563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#!/usr/bin/perl -w 2563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 3563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. 4231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block# Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> 5d0825bca7fe65beaee391d30da42e937db621564Steve Block# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) 6563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 7563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Redistribution and use in source and binary forms, with or without 8563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# modification, are permitted provided that the following conditions 9563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# are met: 10563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 11563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 1. Redistributions of source code must retain the above copyright 12563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# notice, this list of conditions and the following disclaimer. 13563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 2. Redistributions in binary form must reproduce the above copyright 14563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# notice, this list of conditions and the following disclaimer in the 15563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# documentation and/or other materials provided with the distribution. 16563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 17563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# its contributors may be used to endorse or promote products derived 18563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# from this software without specific prior written permission. 19563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 20563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 21563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 24563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 31563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# "unpatch" script for WebKit Open Source Project, used to remove patches. 32563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 33563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Differences from invoking "patch -p0 -R": 34563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 35563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles added files (does a svn revert with additional logic to handle local changes). 36563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles added directories (does a svn revert and a rmdir). 37563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles removed files (does a svn revert with additional logic to handle local changes). 38563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles removed directories (does a svn revert). 39563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Paths from Index: lines are used rather than the paths on the patch lines, which 40563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# makes patches generated by "cvs diff" work (increasingly unimportant since we 41563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# use Subversion now). 42563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# ChangeLog patches use --fuzz=3 to prevent rejects, and the entry date is reset in 43563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# the patch before it is applied (svn-apply sets it when applying a patch). 44563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles binary files (requires patches made by svn-create-patch). 45563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles copied and moved files (requires patches made by svn-create-patch). 46563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles git-diff patches (without binary changes) created at the top-level directory 47563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 48563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Missing features: 49563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 50563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handle property changes. 51563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handle copied and moved directories (would require patches made by svn-create-patch). 52563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Use version numbers in the patch file and do a 3-way merge. 53563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# When reversing an addition, check that the file matches what's being removed. 54563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Notice a patch that's being unapplied at the "wrong level" and make it work anyway. 55563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Do a dry run on the whole patch and don't do anything if part of the patch is 56563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# going to fail (probably too strict unless we exclude ChangeLog). 57563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handle git-diff patches with binary changes 58563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 59563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse strict; 60563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse warnings; 61563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 62563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Cwd; 63563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Digest::MD5; 64563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Fcntl qw(:DEFAULT :seek); 65563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::Basename; 66563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::Spec; 67563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::Temp qw(tempfile); 68563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Getopt::Long; 69563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 70231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockuse FindBin; 71231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockuse lib $FindBin::Bin; 72231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockuse VCSUtils; 73231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block 74563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub checksum($); 75563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub patch($); 76563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub revertDirectories(); 77563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub unapplyPatch($$;$); 78563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub unsetChangeLogDate($$); 79563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 80d0825bca7fe65beaee391d30da42e937db621564Steve Blockmy $force = 0; 81563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy $showHelp = 0; 82d0825bca7fe65beaee391d30da42e937db621564Steve Block 83d0825bca7fe65beaee391d30da42e937db621564Steve Blockmy $optionParseSuccess = GetOptions( 84d0825bca7fe65beaee391d30da42e937db621564Steve Block "force!" => \$force, 85d0825bca7fe65beaee391d30da42e937db621564Steve Block "help!" => \$showHelp 86d0825bca7fe65beaee391d30da42e937db621564Steve Block); 87d0825bca7fe65beaee391d30da42e937db621564Steve Block 88d0825bca7fe65beaee391d30da42e937db621564Steve Blockif (!$optionParseSuccess || $showHelp) { 89d0825bca7fe65beaee391d30da42e937db621564Steve Block print STDERR basename($0) . " [-h|--help] [--force] patch1 [patch2 ...]\n"; 90563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark exit 1; 91563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 92563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 93d0825bca7fe65beaee391d30da42e937db621564Steve Blockmy $globalExitStatus = 0; 94d0825bca7fe65beaee391d30da42e937db621564Steve Block 95d0825bca7fe65beaee391d30da42e937db621564Steve Blockmy $repositoryRootPath = determineVCSRoot(); 96231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block 97563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy @copiedFiles; 98563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy %directoriesToCheck; 99563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 10021939df44de1705786c545cd1bf519d47250322dBen Murdoch# Need to use a typeglob to pass the file handle as a parameter, 10121939df44de1705786c545cd1bf519d47250322dBen Murdoch# otherwise get a bareword error. 10221939df44de1705786c545cd1bf519d47250322dBen Murdochmy @diffHashRefs = parsePatch(*ARGV); 103563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 10421939df44de1705786c545cd1bf519d47250322dBen Murdochprint "Parsed " . @diffHashRefs . " diffs from patch file(s).\n"; 10521939df44de1705786c545cd1bf519d47250322dBen Murdoch 10621939df44de1705786c545cd1bf519d47250322dBen Murdochmy $preparedPatchHash = prepareParsedPatch($force, @diffHashRefs); 10721939df44de1705786c545cd1bf519d47250322dBen Murdoch 10821939df44de1705786c545cd1bf519d47250322dBen Murdochmy @copyDiffHashRefs = @{$preparedPatchHash->{copyDiffHashRefs}}; 10921939df44de1705786c545cd1bf519d47250322dBen Murdochmy @nonCopyDiffHashRefs = @{$preparedPatchHash->{nonCopyDiffHashRefs}}; 11021939df44de1705786c545cd1bf519d47250322dBen Murdoch 11121939df44de1705786c545cd1bf519d47250322dBen Murdochfor my $diffHashRef (@nonCopyDiffHashRefs) { 11221939df44de1705786c545cd1bf519d47250322dBen Murdoch patch($diffHashRef); 113563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 114563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 115563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handle copied and moved files last since they may have had post-copy changes that have now been unapplied 11621939df44de1705786c545cd1bf519d47250322dBen Murdochfor my $diffHashRef (@copyDiffHashRefs) { 11721939df44de1705786c545cd1bf519d47250322dBen Murdoch patch($diffHashRef); 118563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 119563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 120231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockif (isSVN()) { 121231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block revertDirectories(); 122231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block} 123563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 124d0825bca7fe65beaee391d30da42e937db621564Steve Blockexit $globalExitStatus; 125563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 126563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub checksum($) 127563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 128563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $file = shift; 129563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark open(FILE, $file) or die "Can't open '$file': $!"; 130563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark binmode(FILE); 131563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $checksum = Digest::MD5->new->addfile(*FILE)->hexdigest(); 132563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close(FILE); 133563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $checksum; 134563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 135563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 13621939df44de1705786c545cd1bf519d47250322dBen Murdoch# Args: 13721939df44de1705786c545cd1bf519d47250322dBen Murdoch# $diffHashRef: a diff hash reference of the type returned by parsePatch(). 138563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub patch($) 139563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 14021939df44de1705786c545cd1bf519d47250322dBen Murdoch my ($diffHashRef) = @_; 14121939df44de1705786c545cd1bf519d47250322dBen Murdoch 1420617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen # Make sure $patch is initialized to some value. There is no 1430617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen # svnConvertedText when reversing an svn copy/move. 1440617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen my $patch = $diffHashRef->{svnConvertedText} || ""; 14521939df44de1705786c545cd1bf519d47250322dBen Murdoch 14621939df44de1705786c545cd1bf519d47250322dBen Murdoch my $fullPath = $diffHashRef->{indexPath}; 14721939df44de1705786c545cd1bf519d47250322dBen Murdoch my $isSvnBinary = $diffHashRef->{isBinary} && $diffHashRef->{isSvn}; 148563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 149563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $directoriesToCheck{dirname($fullPath)} = 1; 150563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 151563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $deletion = 0; 152563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $addition = 0; 153563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 1546c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen $addition = 1 if ($diffHashRef->{isNew} || $diffHashRef->{copiedFromPath} || $patch =~ /\n@@ -0,0 .* @@/); 1556c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen $deletion = 1 if ($diffHashRef->{isDeletion} || $patch =~ /\n@@ .* \+0,0 @@/); 156563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 15721939df44de1705786c545cd1bf519d47250322dBen Murdoch if (!$addition && !$deletion && !$isSvnBinary) { 158563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Standard patch, patch tool can handle this. 159563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (basename($fullPath) eq "ChangeLog") { 160563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $changeLogDotOrigExisted = -f "${fullPath}.orig"; 161a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch my $changeLogHash = fixChangeLogPatch($patch); 162a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch unapplyPatch(unsetChangeLogDate($fullPath, $changeLogHash->{patch}), $fullPath, ["--fuzz=3"]); 163563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); 164563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } else { 165563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark unapplyPatch($patch, $fullPath); 166563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 167563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } else { 168563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Either a deletion, an addition or a binary change. 169563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 17021939df44de1705786c545cd1bf519d47250322dBen Murdoch # FIXME: Add support for Git binary files. 17121939df44de1705786c545cd1bf519d47250322dBen Murdoch if ($isSvnBinary) { 172563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Reverse binary change 173563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark unlink($fullPath) if (-e $fullPath); 174563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "svn", "revert", $fullPath; 175563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } elsif ($deletion) { 176563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Reverse deletion 177563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark rename($fullPath, "$fullPath.orig") if -e $fullPath; 178563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 179563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark unapplyPatch($patch, $fullPath); 180563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 181563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # If we don't ask for the filehandle here, we always get a warning. 182563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($fh, $tempPath) = tempfile(basename($fullPath) . "-XXXXXXXX", 183563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark DIR => dirname($fullPath), UNLINK => 1); 184563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close($fh); 185563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 186563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Keep the version from the patch in case it's different from svn. 187563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark rename($fullPath, $tempPath); 188563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "svn", "revert", $fullPath; 189563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark rename($tempPath, $fullPath); 190563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 191563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # This works around a bug in the svn client. 192563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # [Issue 1960] file modifications get lost due to FAT 2s time resolution 193563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # http://subversion.tigris.org/issues/show_bug.cgi?id=1960 194563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "touch", $fullPath; 195563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 196563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Remove $fullPath.orig if it is the same as $fullPath 197563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); 198563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 199563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Show status if the file is modifed 200563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "svn", "stat", $fullPath; 201563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } else { 202563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Reverse addition 20321939df44de1705786c545cd1bf519d47250322dBen Murdoch # 20421939df44de1705786c545cd1bf519d47250322dBen Murdoch # FIXME: This should use the same logic as svn-apply's deletion 20521939df44de1705786c545cd1bf519d47250322dBen Murdoch # code. In particular, svn-apply's scmRemove() subroutine 20621939df44de1705786c545cd1bf519d47250322dBen Murdoch # should be used here. 2070617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen unapplyPatch($patch, $fullPath, ["--force"]) if $patch; 208563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark unlink($fullPath) if -z $fullPath; 209563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "svn", "revert", $fullPath; 210563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 211563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 21221939df44de1705786c545cd1bf519d47250322dBen Murdoch 21321939df44de1705786c545cd1bf519d47250322dBen Murdoch scmToggleExecutableBit($fullPath, -1 * $diffHashRef->{executableBitDelta}) if defined($diffHashRef->{executableBitDelta}); 214563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 215563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 216563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub revertDirectories() 217563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 218d0825bca7fe65beaee391d30da42e937db621564Steve Block chdir $repositoryRootPath; 219563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my %checkedDirectories; 220563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark foreach my $path (reverse sort keys %directoriesToCheck) { 221563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @dirs = File::Spec->splitdir($path); 222563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (scalar @dirs) { 223563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $dir = File::Spec->catdir(@dirs); 224563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark pop(@dirs); 225563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark next if (exists $checkedDirectories{$dir}); 226563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if (-d $dir) { 227563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $svnOutput = svnStatus($dir); 228563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($svnOutput && $svnOutput =~ m#A\s+$dir\n#) { 229563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "svn", "revert", $dir; 230563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark rmdir $dir; 231563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 232563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark elsif ($svnOutput && $svnOutput =~ m#D\s+$dir\n#) { 233563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark system "svn", "revert", $dir; 234563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 235563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark else { 236563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Modification 237563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print $svnOutput if $svnOutput; 238563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 239563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $checkedDirectories{$dir} = 1; 240563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 241563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark else { 242563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark die "'$dir' is not a directory"; 243563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 244563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 245563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 246563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 247563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 248d0825bca7fe65beaee391d30da42e937db621564Steve Block# Args: 249d0825bca7fe65beaee391d30da42e937db621564Steve Block# $patch: a patch string. 250d0825bca7fe65beaee391d30da42e937db621564Steve Block# $pathRelativeToRoot: the path of the file to be patched, relative to the 251d0825bca7fe65beaee391d30da42e937db621564Steve Block# repository root. This should normally be the path 252d0825bca7fe65beaee391d30da42e937db621564Steve Block# found in the patch's "Index:" line. 253d0825bca7fe65beaee391d30da42e937db621564Steve Block# $options: a reference to an array of options to pass to the patch command. 254d0825bca7fe65beaee391d30da42e937db621564Steve Block# Do not include --reverse in this array. 255563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub unapplyPatch($$;$) 256563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 257d0825bca7fe65beaee391d30da42e937db621564Steve Block my ($patch, $pathRelativeToRoot, $options) = @_; 258d0825bca7fe65beaee391d30da42e937db621564Steve Block 259d0825bca7fe65beaee391d30da42e937db621564Steve Block my $optionalArgs = {options => $options, ensureForce => $force, shouldReverse => 1}; 260d0825bca7fe65beaee391d30da42e937db621564Steve Block 261d0825bca7fe65beaee391d30da42e937db621564Steve Block my $exitStatus = runPatchCommand($patch, $repositoryRootPath, $pathRelativeToRoot, $optionalArgs); 262d0825bca7fe65beaee391d30da42e937db621564Steve Block 263d0825bca7fe65beaee391d30da42e937db621564Steve Block if ($exitStatus) { 264d0825bca7fe65beaee391d30da42e937db621564Steve Block $globalExitStatus = $exitStatus; 265d0825bca7fe65beaee391d30da42e937db621564Steve Block } 266563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 267563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 268563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub unsetChangeLogDate($$) 269563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 270563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $fullPath = shift; 271563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $patch = shift; 272563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $newDate; 273563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark sysopen(CHANGELOG, $fullPath, O_RDONLY) or die "Failed to open $fullPath: $!"; 274563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark sysseek(CHANGELOG, 0, SEEK_SET); 275563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $byteCount = sysread(CHANGELOG, $newDate, 10); 276563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark die "Failed reading $fullPath: $!" if !$byteCount || $byteCount != 10; 277563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close(CHANGELOG); 278563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $patch =~ s/(\n\+)\d{4}-[^-]{2}-[^-]{2}( )/$1$newDate$2/; 279563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $patch; 280563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 281