blob: f96d7b38c38257ef0eb1135ae007f8ce00c3a52c [file] [log] [blame]
#!/usr/bin/perl -w
# Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
# its contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# "patch" script for WebKit Open Source Project, used to apply patches.
# Differences from invoking "patch -p0":
#
# Handles added files (does a svn add).
# Handles removed files (does a svn rm).
# Has mode where it will roll back to svn version numbers in the patch file so svn
# can do a 3-way merge.
# Paths from Index: lines are used rather than the paths on the patch lines, which
# makes patches generated by "cvs diff" work (increasingly unimportant since we
# use Subversion now).
# ChangeLog patches use --fuzz=3 to prevent rejects.
#
# Missing features:
#
# Handle property changes.
# Handle binary files (requires patches made by svn-create-patch).
# Handle file moves (requires patches made by svn-create-patch).
# When doing a removal, check that old file matches what's being removed.
# Notice a patch that's being applied at the "wrong level" and make it work anyway.
# Do a dry run on the whole patch and don't do anything if part of the patch is
# going to fail (probably too strict unless we do the ChangeLog thing).
use strict;
use Cwd;
use Getopt::Long;
my $merge = 0;
GetOptions("merge" => \$merge);
my $startDir = getcwd();
my @patches;
my %versions;
my $indexPath;
my $patch;
while (<>) {
s/\r//g;
chomp;
if (/^Index: (.+)/) {
$indexPath = $1;
if ($patch) {
push @patches, $patch;
$patch = "";
}
}
if ($indexPath) {
# Fix paths on diff, ---, and +++ lines to match preceding Index: line.
s/\S+$/$indexPath/ if /^diff/;
s/^--- \S+/--- $indexPath/;
if (s/^\+\+\+ \S+/+++ $indexPath/) {
$indexPath = "";
}
}
if (/^--- .+\(revision (\d+)\)$/) {
$versions{$indexPath} = $1 if( $1 != 0 );
}
$patch .= $_;
$patch .= "\n";
}
push @patches, $patch if $patch;
if ($merge) {
for my $file (sort keys %versions) {
print "Getting version $versions{$file} of $file\n";
$file =~ m|^(([^/\n]*/)*)([^/\n]+)$| or die;
my ($prefix, $base) = ($1, $3);
if ($prefix) {
chdir $prefix or die;
}
system "svn update -r $versions{$file} $base";
chdir $startDir;
}
}
for $patch (@patches) {
patch($patch);
}
sub applyPatch
{
my ($patch, $fullpath, $options) = @_;
$options = [] if (! $options);
my $command = "patch " . join(" ", "-p0", @{$options});
open PATCH, "| $command" or die "Failed to patch $fullpath\n";
print PATCH $patch;
close PATCH;
}
sub patch
{
my ($patch) = @_;
return if !$patch;
$patch =~ m|^Index: ((([^/\n]*/)*)([^/\n]+))| or die "Failed to find Index: in \"$patch\"\n";
my ($fullpath, $prefix, $base) = ($1, $2, $4);
my $deletion = 0;
my $addition = 0;
$addition = 1 if $patch =~ /\n--- .+\(revision 0\)\n/;
$deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/;
if (!$addition && !$deletion) {
# Standard patch, patch tool can handle this.
if ($base eq "ChangeLog") {
my $changeLogDotOrigExisted = -f "${fullpath}.orig";
applyPatch($patch, $fullpath, ["--fuzz=3"]);
unlink("${fullpath}.orig") if (! $changeLogDotOrigExisted);
}
else {
applyPatch($patch, $fullpath);
}
} else {
# Either a deletion or an addition.
# Change directory down into the directory in question.
chdirAddingDirectoriesIfNeeded($prefix);
if ($deletion) {
# Deletion.
system "svn", "rm", $base;
} else {
# Addition.
my $file = $patch;
if ($file !~ s/^(.*\n)*@@[^\n]+@@\n//) {
# Empty file.
$file = "";
} else {
# Non-empty file: Remove leading + signs.
$file =~ s/^\+//;
$file =~ s/\n\+/\n/g;
}
open FILE, ">", $base or die;
print FILE $file;
close FILE;
system "svn", "add", "$base";
}
chdir $startDir if $prefix;
}
}
sub chdirAddingDirectoriesIfNeeded
{
my $path = shift;
my @dirs = split('/', $path);
while (my $dir = shift @dirs) {
if (!-x $dir) {
mkdir $dir or die "Failed create required directory: $dir for path: $path\n";
system "svn", "add", "$dir";
}
chdir $dir or die "Failed to chdir to $dir\n";
}
}