summaryrefslogtreecommitdiff
path: root/Tools/Scripts/VCSUtils.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/Scripts/VCSUtils.pm')
-rw-r--r--Tools/Scripts/VCSUtils.pm187
1 files changed, 157 insertions, 30 deletions
diff --git a/Tools/Scripts/VCSUtils.pm b/Tools/Scripts/VCSUtils.pm
index 305d65bc0..214150d12 100644
--- a/Tools/Scripts/VCSUtils.pm
+++ b/Tools/Scripts/VCSUtils.pm
@@ -1,4 +1,4 @@
-# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+# Copyright (C) 2007-2013, 2015 Apple Inc. All rights reserved.
# Copyright (C) 2009, 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
# Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
# Copyright (C) 2012 Daniel Bates (dbates@intudata.com)
@@ -12,7 +12,7 @@
# 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
+# 3. Neither the name of Apple 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.
#
@@ -50,7 +50,6 @@ BEGIN {
&callSilently
&canonicalizePath
&changeLogEmailAddress
- &changeLogFileName
&changeLogName
&chdirReturningRelativePath
&decodeGitBinaryChunk
@@ -61,11 +60,14 @@ BEGIN {
&exitStatus
&fixChangeLogPatch
&gitBranch
+ &gitDirectory
+ &gitTreeDirectory
&gitdiff2svndiff
&isGit
&isGitSVN
&isGitBranchBuild
&isGitDirectory
+ &isGitSVNDirectory
&isSVN
&isSVNDirectory
&isSVNVersion16OrNewer
@@ -89,6 +91,7 @@ BEGIN {
&toWindowsLineEndings
&gitCommitForSVNRevision
&listOfChangedFilesBetweenRevisions
+ &unixPath
);
%EXPORT_TAGS = ( );
@EXPORT_OK = ();
@@ -107,6 +110,7 @@ my $svnVersion;
# 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+)$#;
@@ -125,7 +129,7 @@ my $svnPropertyValueNoNewlineRegEx = qr#\ No newline at end of property#;
sub exitStatus($)
{
my ($returnvalue) = @_;
- if ($^O eq "MSWin32") {
+ if (isWindows()) {
return $returnvalue >> 8;
}
if (!WIFEXITED($returnvalue)) {
@@ -224,15 +228,27 @@ sub isGit()
return $isGit;
}
-sub isGitSVN()
+sub isGitSVNDirectory($)
{
- return $isGitSVN if defined $isGitSVN;
+ my ($directory) = @_;
+
+ my $savedWorkingDirectory = Cwd::getcwd();
+ chdir($directory);
# There doesn't seem to be an officially documented way to determine
# if you're in a git-svn checkout. The best suggestions seen so far
# all use something like the following:
my $output = `git config --get svn-remote.svn.fetch 2>& 1`;
- $isGitSVN = $output ne '';
+ $isGitSVN = exitStatus($?) == 0 && $output ne "";
+ chdir($savedWorkingDirectory);
+ return $isGitSVN;
+}
+
+sub isGitSVN()
+{
+ return $isGitSVN if defined $isGitSVN;
+
+ $isGitSVN = isGitSVNDirectory(".");
return $isGitSVN;
}
@@ -242,6 +258,12 @@ sub gitDirectory()
return $result;
}
+sub gitTreeDirectory()
+{
+ chomp(my $result = `git rev-parse --show-toplevel`);
+ return $result;
+}
+
sub gitBisectStartBranch()
{
my $bisectStartFile = File::Spec->catfile(gitDirectory(), "BISECT_START");
@@ -314,7 +336,7 @@ sub svnVersion()
sub isSVNVersion16OrNewer()
{
my $version = svnVersion();
- return eval "v$version" ge v1.6;
+ return "v$version" ge v1.6;
}
sub chdirReturningRelativePath($)
@@ -375,7 +397,9 @@ sub determineSVNRoot()
sub determineVCSRoot()
{
if (isGit()) {
- return dirname(gitDirectory());
+ # This is the working tree root. If WebKit is a submodule,
+ # then the relevant metadata directory is somewhere else.
+ return gitTreeDirectory();
}
if (!isSVN()) {
@@ -460,6 +484,17 @@ sub makeFilePathRelative($)
sub normalizePath($)
{
my ($path) = @_;
+ if (isWindows()) {
+ $path =~ s/\//\\/g;
+ } else {
+ $path =~ s/\\/\//g;
+ }
+ return $path;
+}
+
+sub unixPath($)
+{
+ my ($path) = @_;
$path =~ s/\\/\//g;
return $path;
}
@@ -908,6 +943,103 @@ sub parseSvnDiffHeader($$)
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.
#
@@ -946,6 +1078,7 @@ sub parseDiffHeader($$)
my $header; # This is a hash ref.
my $isGit;
my $isSvn;
+ my $isUnified;
my $lastReadLine;
if ($line =~ $svnDiffStartRegEx) {
@@ -954,12 +1087,16 @@ sub parseDiffHeader($$)
} 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);
}
@@ -1044,6 +1181,10 @@ sub parseDiff($$;$)
# 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;
@@ -1880,7 +2021,7 @@ sub mergeChangeLogs($$$)
sub gitConfig($)
{
- return unless $isGit;
+ return unless isGit();
my ($config) = @_;
@@ -1889,23 +2030,6 @@ sub gitConfig($)
return $result;
}
-sub changeLogSuffix()
-{
- my $rootPath = determineVCSRoot();
- my $changeLogSuffixFile = File::Spec->catfile($rootPath, ".changeLogSuffix");
- return "" if ! -e $changeLogSuffixFile;
- open FILE, $changeLogSuffixFile or die "Could not open $changeLogSuffixFile: $!";
- my $changeLogSuffix = <FILE>;
- chomp $changeLogSuffix;
- close FILE;
- return $changeLogSuffix;
-}
-
-sub changeLogFileName()
-{
- return "ChangeLog" . changeLogSuffix()
-}
-
sub changeLogNameError($)
{
my ($message) = @_;
@@ -1919,7 +2043,10 @@ sub changeLogNameError($)
sub changeLogName()
{
- my $name = $ENV{CHANGE_LOG_NAME} || $ENV{REAL_NAME} || gitConfig("user.name") || (split /\s*,\s*/, (getpwuid $<)[6])[0];
+ my $name = $ENV{CHANGE_LOG_NAME} || $ENV{REAL_NAME} || gitConfig("user.name");
+ if (not $name and !isWindows()) {
+ $name = (split /\s*,\s*/, (getpwuid $<)[6])[0];
+ }
changeLogNameError("Failed to determine ChangeLog name.") unless $name;
# getpwuid seems to always succeed on windows, returning the username instead of the full name. This check will catch that case.
@@ -2020,8 +2147,8 @@ sub decodeGitBinaryPatch($$)
# Then, content of the chunk comes. To decode the content, we
# need decode it with base85 first, and then zlib.
my $gitPatchRegExp = '(literal|delta) ([0-9]+)\n([A-Za-z0-9!#$%&()*+-;<=>?@^_`{|}~\\n]*?)\n\n';
- if ($contents !~ m"\nGIT binary patch\n$gitPatchRegExp$gitPatchRegExp\Z") {
- die "$fullPath: unknown git binary patch format"
+ if ($contents !~ m"\nGIT binary patch\n$gitPatchRegExp$gitPatchRegExp(\Z|-- \n)") {
+ return ();
}
my $binaryChunkType = $1;