15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#!/usr/bin/perl -w 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Redistribution and use in source and binary forms, with or without 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# modification, are permitted provided that the following conditions 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# are met: 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 1. Redistributions of source code must retain the above copyright 12fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch# notice, this list of conditions and the following disclaimer. 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 2. Redistributions in binary form must reproduce the above copyright 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer in the 15fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch# documentation and/or other materials provided with the distribution. 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# its contributors may be used to endorse or promote products derived 18fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch# from this software without specific prior written permission. 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# "unpatch" script for WebKit Open Source Project, used to remove patches. 325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Differences from invoking "patch -p0 -R": 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 35fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch# Handles added files (does a svn revert with additional logic to handle local changes). 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handles added directories (does a svn revert and a rmdir). 37fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch# Handles removed files (does a svn revert with additional logic to handle local changes). 38fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch# Handles removed directories (does a svn revert). 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Paths from Index: lines are used rather than the paths on the patch lines, which 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# makes patches generated by "cvs diff" work (increasingly unimportant since we 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# use Subversion now). 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ChangeLog patches use --fuzz=3 to prevent rejects, and the entry date is reset in 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# the patch before it is applied (svn-apply sets it when applying a patch). 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handles binary files (requires patches made by svn-create-patch). 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handles copied and moved files (requires patches made by svn-create-patch). 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handles git-diff patches (without binary changes) created at the top-level directory 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Missing features: 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handle property changes. 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handle copied and moved directories (would require patches made by svn-create-patch). 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Use version numbers in the patch file and do a 3-way merge. 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# When reversing an addition, check that the file matches what's being removed. 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Notice a patch that's being unapplied at the "wrong level" and make it work anyway. 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Do a dry run on the whole patch and don't do anything if part of the patch is 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# going to fail (probably too strict unless we exclude ChangeLog). 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handle git-diff patches with binary changes 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use strict; 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use warnings; 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use Cwd; 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use Digest::MD5; 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use Fcntl qw(:DEFAULT :seek); 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Basename; 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Spec; 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Temp qw(tempfile); 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use Getopt::Long; 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use FindBin; 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use lib $FindBin::Bin; 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use VCSUtils; 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub checksum($); 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub patch($); 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub revertDirectories(); 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub unapplyPatch($$;$); 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub unsetChangeLogDate($$); 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $force = 0; 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $showHelp = 0; 825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $optionParseSuccess = GetOptions( 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) "force!" => \$force, 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) "help!" => \$showHelp 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)); 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)if (!$optionParseSuccess || $showHelp) { 895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) print STDERR basename($0) . " [-h|--help] [--force] patch1 [patch2 ...]\n"; 905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) exit 1; 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $globalExitStatus = 0; 945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $repositoryRootPath = determineVCSRoot(); 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my @copiedFiles; 985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my %directoriesToCheck; 995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Need to use a typeglob to pass the file handle as a parameter, 1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# otherwise get a bareword error. 1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my @diffHashRefs = parsePatch(*ARGV); 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)print "Parsed " . @diffHashRefs . " diffs from patch file(s).\n"; 1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $preparedPatchHash = prepareParsedPatch($force, @diffHashRefs); 1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my @copyDiffHashRefs = @{$preparedPatchHash->{copyDiffHashRefs}}; 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my @nonCopyDiffHashRefs = @{$preparedPatchHash->{nonCopyDiffHashRefs}}; 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)for my $diffHashRef (@nonCopyDiffHashRefs) { 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) patch($diffHashRef); 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Handle copied and moved files last since they may have had post-copy changes that have now been unapplied 1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)for my $diffHashRef (@copyDiffHashRefs) { 1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) patch($diffHashRef); 1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)if (isSVN()) { 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) revertDirectories(); 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)exit $globalExitStatus; 1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub checksum($) 1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $file = shift; 1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) open(FILE, $file) or die "Can't open '$file': $!"; 1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) binmode(FILE); 1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $checksum = Digest::MD5->new->addfile(*FILE)->hexdigest(); 1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) close(FILE); 1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return $checksum; 1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Args: 1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $diffHashRef: a diff hash reference of the type returned by parsePatch(). 1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub patch($) 1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my ($diffHashRef) = @_; 1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Make sure $patch is initialized to some value. There is no 1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # svnConvertedText when reversing an svn copy/move. 1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $patch = $diffHashRef->{svnConvertedText} || ""; 1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $fullPath = $diffHashRef->{indexPath}; 1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $isSvnBinary = $diffHashRef->{isBinary} && $diffHashRef->{isSvn}; 1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $hasTextChunks = $patch && $diffHashRef->{numTextChunks}; 1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) $directoriesToCheck{dirname($fullPath)} = 1; 1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $deletion = 0; 1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $addition = 0; 1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) $addition = 1 if ($diffHashRef->{isNew} || $diffHashRef->{copiedFromPath} || $patch =~ /\n@@ -0,0 .* @@/); 1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) $deletion = 1 if ($diffHashRef->{isDeletion} || $patch =~ /\n@@ .* \+0,0 @@/); 1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!$addition && !$deletion && !$isSvnBinary && $hasTextChunks) { 1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Standard patch, patch tool can handle this. 1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (basename($fullPath) eq "ChangeLog") { 1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $changeLogDotOrigExisted = -f "${fullPath}.orig"; 1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $changeLogHash = fixChangeLogPatch($patch); 1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unapplyPatch(unsetChangeLogDate($fullPath, $changeLogHash->{patch}), $fullPath, ["--fuzz=3"]); 1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); 1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unapplyPatch($patch, $fullPath); 1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Either a deletion, an addition or a binary change. 1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $escapedFullPath = escapeSubversionPath($fullPath); 1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # FIXME: Add support for Git binary files. 1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if ($isSvnBinary) { 1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Reverse binary change 1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unlink($fullPath) if (-e $fullPath); 1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "svn", "revert", $escapedFullPath; 1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } elsif ($deletion) { 1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Reverse deletion 1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rename($fullPath, "$fullPath.orig") if -e $fullPath; 1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unapplyPatch($patch, $fullPath); 1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # If we don't ask for the filehandle here, we always get a warning. 1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my ($fh, $tempPath) = tempfile(basename($fullPath) . "-XXXXXXXX", 1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) DIR => dirname($fullPath), UNLINK => 1); 1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) close($fh); 1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Keep the version from the patch in case it's different from svn. 1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rename($fullPath, $tempPath); 1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "svn", "revert", $escapedFullPath; 1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rename($tempPath, $fullPath); 1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # This works around a bug in the svn client. 1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # [Issue 1960] file modifications get lost due to FAT 2s time resolution 1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # http://subversion.tigris.org/issues/show_bug.cgi?id=1960 1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "touch", $fullPath; 1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Remove $fullPath.orig if it is the same as $fullPath 1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); 2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Show status if the file is modifed 2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "svn", "stat", $escapedFullPath; 2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } elsif ($addition) { 2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Reverse addition 2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # 2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # FIXME: This should use the same logic as svn-apply's deletion 2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # code. In particular, svn-apply's scmRemove() subroutine 2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # should be used here. 2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unapplyPatch($patch, $fullPath, ["--force"]) if $patch; 2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unlink($fullPath) if -z $fullPath; 2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "svn", "revert", $escapedFullPath; 2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) scmToggleExecutableBit($fullPath, -1 * $diffHashRef->{executableBitDelta}) if defined($diffHashRef->{executableBitDelta}); 2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub revertDirectories() 2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) chdir $repositoryRootPath; 2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my %checkedDirectories; 2225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) foreach my $path (reverse sort keys %directoriesToCheck) { 2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my @dirs = File::Spec->splitdir($path); 2245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) while (scalar @dirs) { 2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $dir = File::Spec->catdir(@dirs); 2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) pop(@dirs); 2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) next if (exists $checkedDirectories{$dir}); 2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (-d $dir) { 2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $svnOutput = svnStatus($dir); 2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $escapedDir = escapeSubversionPath($dir); 2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if ($svnOutput && $svnOutput =~ m#A\s+$dir\n#) { 2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "svn", "revert", $escapedDir; 2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rmdir $dir; 2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) elsif ($svnOutput && $svnOutput =~ m#D\s+$dir\n#) { 2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) system "svn", "revert", $escapedDir; 2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else { 2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Modification 2405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) print $svnOutput if $svnOutput; 2415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) $checkedDirectories{$dir} = 1; 2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else { 2455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) die "'$dir' is not a directory"; 2465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Args: 2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $patch: a patch string. 2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $pathRelativeToRoot: the path of the file to be patched, relative to the 2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# repository root. This should normally be the path 2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# found in the patch's "Index:" line. 2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $options: a reference to an array of options to pass to the patch command. 2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Do not include --reverse in this array. 2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub unapplyPatch($$;$) 2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my ($patch, $pathRelativeToRoot, $options) = @_; 2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $optionalArgs = {options => $options, ensureForce => $force, shouldReverse => 1}; 2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $exitStatus = runPatchCommand($patch, $repositoryRootPath, $pathRelativeToRoot, $optionalArgs); 2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if ($exitStatus) { 2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) $globalExitStatus = $exitStatus; 2685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub unsetChangeLogDate($$) 2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $fullPath = shift; 2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $patch = shift; 2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $newDate; 2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) sysopen(CHANGELOG, $fullPath, O_RDONLY) or die "Failed to open $fullPath: $!"; 2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) sysseek(CHANGELOG, 0, SEEK_SET); 2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) my $byteCount = sysread(CHANGELOG, $newDate, 10); 2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) die "Failed reading $fullPath: $!" if !$byteCount || $byteCount != 10; 2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) close(CHANGELOG); 2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) $patch =~ s/(\n\+)\d{4}-[^-]{2}-[^-]{2}( )/$1$newDate$2/; 2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return $patch; 2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 284