+2015-10-30 Dana Burkart <dburkart@apple.com>
+
+ svn-apply should handle unified diffs
+ https://bugs.webkit.org/show_bug.cgi?id=150650
+
+ Reviewed by Darin Adler.
+
+ * Scripts/VCSUtils.pm:
+ (parseUnifiedDiffHeader):
+ This method parses a unified diff header, and returns a information in the
+ style of parseGitDiffHeader and parseSvnDiffHeader.
+
+ (parseDiffHeader):
+ Teach parseDiffHeader to recognize unified diff headers.
+
+ (parseDiff):
+ Teach parseDiff to recognize unified diffs.
+
+ * Scripts/webkitperl/VCSUtils_unittest/parseDiffHeader.pl:
+ * Scripts/webkitperl/VCSUtils_unittest/parseUnifiedDiffHeader.pl: Added.
+
2015-11-02 Csaba Osztrogonác <ossy@webkit.org>
Fix the FTL JIT build with system LLVM on Linux
# Project time zone for Cupertino, CA, US
my $changeLogTimeZone = "PST8PDT";
+my $unifiedDiffStartRegEx = qr#^--- ([abc]\/)?([^\r\n]+)#;
my $gitDiffStartRegEx = qr#^diff --git [^\r\n]+#;
my $gitDiffStartWithPrefixRegEx = qr#^diff --git \w/(.+) \w/([^\r\n]+)#; # We suppose that --src-prefix and --dst-prefix don't contain a non-word character (\W) and end with '/'.
my $gitDiffStartWithoutPrefixNoSpaceRegEx = qr#^diff --git (\S+) (\S+)$#;
return (\%header, $_);
}
+# Parse the next Unified diff header from the given file handle, and advance
+# the handle so the last line read is the first line after the header.
+#
+# This subroutine dies if given leading junk.
+#
+# Args:
+# $fileHandle: advanced so the last line read from the handle is the first
+# line of the header to parse. This should be a line
+# beginning with "Index:".
+# $line: the line last read from $fileHandle
+#
+# Returns ($headerHashRef, $lastReadLine):
+# $headerHashRef: a hash reference representing a diff header, as follows--
+# indexPath: the path of the target file, which is the path found in
+# the "Index:" line.
+# isNew: the value 1 if the diff is for a new file.
+# isDeletion: the value 1 if the diff is a file deletion.
+# svnConvertedText: the header text converted to a header with the paths
+# in some lines corrected.
+# $lastReadLine: the line last read from $fileHandle.
+sub parseUnifiedDiffHeader($$)
+{
+ my ($fileHandle, $line) = @_;
+
+ $_ = $line;
+
+ my $currentPosition = tell($fileHandle);
+ my $indexLine;
+ my $indexPath;
+ if (/$unifiedDiffStartRegEx/) {
+ # Use $POSTMATCH to preserve the end-of-line character.
+ my $eol = $POSTMATCH;
+
+ $indexPath = $2;
+
+ # In the case of an addition, we look at the next line for the index path
+ if ($indexPath eq "/dev/null") {
+ $_ = <$fileHandle>;
+ if (/^\+\+\+ ([abc]\/)?([^\t\n\r]+)/) {
+ $indexPath = $2;
+ } else {
+ die "Unrecognized unified diff format.";
+ }
+ $_ = $line;
+ }
+
+ $indexLine = "Index: $indexPath$eol"; # Convert to SVN format.
+ } else {
+ die("Could not parse leading \"---\" line: \"$line\".");
+ }
+
+ seek($fileHandle, $currentPosition, 0);
+
+ my $isDeletion;
+ my $isHeaderEnding;
+ my $isNew;
+ my $svnConvertedText = $indexLine;
+ while (1) {
+ # Temporarily strip off any end-of-line characters to simplify
+ # regex matching below.
+ s/([\n\r]+)$//;
+ my $eol = $1;
+
+ if (/^--- \/dev\/null/) {
+ $isNew = 1;
+ } elsif (/^\+\+\+ \/dev\/null/) {
+ $isDeletion = 1;
+ }
+
+ if (/^(---|\+\+\+) ([abc]\/)?([^\t\n\r]+)/) {
+ if ($1 eq "---") {
+ my $prependText = "";
+ $prependText = "new file mode 100644\n" if $isNew;
+ $_ = "${prependText}index 0000000..0000000\n$1 $3";
+ } else {
+ $_ = "$1 $3";
+ $isHeaderEnding = 1;
+ }
+ }
+
+ $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters.
+
+ $currentPosition = tell($fileHandle);
+ $_ = <$fileHandle>; # Not defined if end-of-file reached.
+ last if (!defined($_) || /$unifiedDiffStartRegEx/ || $isHeaderEnding);
+ }
+
+ my %header;
+
+ $header{indexPath} = $indexPath;
+ $header{isDeletion} = $isDeletion if $isDeletion;
+ $header{isNew} = $isNew if $isNew;
+ $header{svnConvertedText} = $svnConvertedText;
+
+ return (\%header, $_);
+}
+
# Parse the next diff header from the given file handle, and advance
# the handle so the last line read is the first line after the header.
#
my $header; # This is a hash ref.
my $isGit;
my $isSvn;
+ my $isUnified;
my $lastReadLine;
if ($line =~ $svnDiffStartRegEx) {
} elsif ($line =~ $gitDiffStartRegEx) {
$isGit = 1;
($header, $lastReadLine) = parseGitDiffHeader($fileHandle, $line);
+ } elsif ($line =~ $unifiedDiffStartRegEx) {
+ $isUnified = 1;
+ ($header, $lastReadLine) = parseUnifiedDiffHeader($fileHandle, $line);
} else {
die("First line of diff does not begin with \"Index:\" or \"diff --git\": \"$line\"");
}
$header->{isGit} = $isGit if $isGit;
$header->{isSvn} = $isSvn if $isSvn;
+ $header->{isUnified} = $isUnified if $isUnified;
return ($header, $lastReadLine);
}
# all diffs in the patch are formatted the same (SVN or Git).
$headerStartRegEx = $gitDiffStartRegEx;
}
+
+ if (!$headerHashRef && ($line =~ $unifiedDiffStartRegEx)) {
+ $headerStartRegEx = $unifiedDiffStartRegEx;
+ }
if ($line =~ $svnPropertiesStartRegEx) {
my $propertyPath = $1;
undef],
expectedNextLine => undef,
},
+####
+# Unified patch cases
+##
+{
+ diffName => "Unified: Modified file",
+ inputText => <<'END',
+--- Foo/bar.h
++++ Foo/bar.h
+END
+ expectedReturn => [
+{
+ svnConvertedText => <<'END',
+Index: Foo/bar.h
+index 0000000..0000000
+--- Foo/bar.h
++++ Foo/bar.h
+END
+ indexPath => 'Foo/bar.h',
+ isUnified => 1,
+},
+undef],
+ expectedNextLine => undef,
+}
);
my $testCasesCount = @testCaseHashRefs;
--- /dev/null
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2015 Apple 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+
+# Unit tests for parseUnifiedDiffHeader()
+
+use strict;
+use warnings;
+
+use Test::More;
+use VCSUtils;
+
+my @testCaseHashRefs = (
+{
+ diffName => "Modified file",
+ inputText => <<'END',
+--- Foo/bar.h
++++ Foo/bar.h
+@@ -304,6 +304,6 @@ void someKindOfFunction()
+leading
+text
+context
+-file contents
++new file contents
+trailing
+textcontext
+END
+ expectedReturn => [
+ {
+ svnConvertedText => <<'END',
+Index: Foo/bar.h
+index 0000000..0000000
+--- Foo/bar.h
++++ Foo/bar.h
+END
+ indexPath => 'Foo/bar.h',
+ },
+ "@@ -304,6 +304,6 @@ void someKindOfFunction()\n"],
+ expectedNextLine => "leading\n",
+},
+{
+ diffName => "Modified file with spaces in name",
+ inputText => <<'END',
+--- Foo/bar baz.h
++++ Foo/bar baz.h
+@@ -304,6 +304,6 @@ void someKindOfFunction()
+leading
+text
+context
+-file contents
++new file contents
+trailing
+textcontext
+END
+ expectedReturn => [
+ {
+ svnConvertedText => <<'END',
+Index: Foo/bar baz.h
+index 0000000..0000000
+--- Foo/bar baz.h
++++ Foo/bar baz.h
+END
+ indexPath => 'Foo/bar baz.h',
+ },
+ "@@ -304,6 +304,6 @@ void someKindOfFunction()\n"],
+ expectedNextLine => "leading\n",
+},
+{
+ diffName => "Modified file with git-style indices",
+ inputText => <<'END',
+--- a/Foo/bar.h
++++ b/Foo/bar.h
+@@ -304,6 +304,6 @@ void someKindOfFunction()
+leading
+text
+context
+-file contents
++new file contents
+trailing
+textcontext
+END
+ expectedReturn => [
+ {
+ svnConvertedText => <<'END',
+Index: Foo/bar.h
+index 0000000..0000000
+--- Foo/bar.h
++++ Foo/bar.h
+END
+ indexPath => 'Foo/bar.h',
+ },
+ "@@ -304,6 +304,6 @@ void someKindOfFunction()\n"],
+ expectedNextLine => "leading\n",
+},
+{
+ diffName => "Added file",
+ inputText => <<'END',
+--- /dev/null
++++ Foo/bar.h
+@@ -0,0 +1,6 @@
++leading
++text
++context
++new file contents
++trailing
++textcontext
+END
+ expectedReturn => [
+ {
+ svnConvertedText => <<'END',
+Index: Foo/bar.h
+new file mode 100644
+index 0000000..0000000
+--- /dev/null
++++ Foo/bar.h
+END
+ indexPath => 'Foo/bar.h',
+ isNew => 1,
+ },
+ "@@ -0,0 +1,6 @@\n"],
+ expectedNextLine => "+leading\n",
+},
+{
+ diffName => "Removed file",
+ inputText => <<'END',
+--- Foo/bar.h
++++ /dev/null
+@@ -0,0 +1,6 @@
+-leading
+-text
+-context
+-new file contents
+-trailing
+-textcontext
+END
+ expectedReturn => [
+ {
+ svnConvertedText => <<'END',
+Index: Foo/bar.h
+index 0000000..0000000
+--- Foo/bar.h
++++ /dev/null
+END
+ indexPath => 'Foo/bar.h',
+ isDeletion => 1,
+ },
+ "@@ -0,0 +1,6 @@\n"],
+ expectedNextLine => "-leading\n",
+},
+);
+
+
+my $testCasesCount = @testCaseHashRefs;
+plan(tests => 2 * $testCasesCount); # Total number of assertions.
+
+foreach my $testCase (@testCaseHashRefs) {
+ my $testNameStart = "parseUnifiedDiffHeader(): $testCase->{diffName}: comparing";
+
+ my $fileHandle;
+ open($fileHandle, "<", \$testCase->{inputText});
+ my $line = <$fileHandle>;
+
+ my @got = VCSUtils::parseUnifiedDiffHeader($fileHandle, $line);
+ my $expectedReturn = $testCase->{expectedReturn};
+
+ is_deeply(\@got, $expectedReturn, "$testNameStart return value.");
+
+ my $gotNextLine = <$fileHandle>;
+ is($gotNextLine, $testCase->{expectedNextLine}, "$testNameStart next read line.");
+}