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