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.pm98
1 files changed, 90 insertions, 8 deletions
diff --git a/Tools/Scripts/VCSUtils.pm b/Tools/Scripts/VCSUtils.pm
index 6bba77407..b3b8ec290 100644
--- a/Tools/Scripts/VCSUtils.pm
+++ b/Tools/Scripts/VCSUtils.pm
@@ -1,6 +1,7 @@
# Copyright (C) 2007, 2008, 2009 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)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -86,6 +87,8 @@ BEGIN {
&svnRevisionForDirectory
&svnStatus
&toWindowsLineEndings
+ &gitCommitForSVNRevision
+ &listOfChangedFilesBetweenRevisions
);
%EXPORT_TAGS = ( );
@EXPORT_OK = ();
@@ -108,7 +111,8 @@ my $gitDiffStartRegEx = qr#^diff --git (\w/)?(.+) (\w/)?([^\r\n]+)#;
my $svnDiffStartRegEx = qr#^Index: ([^\r\n]+)#;
my $svnPropertiesStartRegEx = qr#^Property changes on: ([^\r\n]+)#; # $1 is normally the same as the index path.
my $svnPropertyStartRegEx = qr#^(Modified|Name|Added|Deleted): ([^\r\n]+)#; # $2 is the name of the property.
-my $svnPropertyValueStartRegEx = qr#^ (\+|-|Merged|Reverse-merged) ([^\r\n]+)#; # $2 is the start of the property's value (which may span multiple lines).
+my $svnPropertyValueStartRegEx = qr#^\s*(\+|-|Merged|Reverse-merged)\s*([^\r\n]+)#; # $2 is the start of the property's value (which may span multiple lines).
+my $svnPropertyValueNoNewlineRegEx = qr#\ No newline at end of property#;
# This method is for portability. Return the system-appropriate exit
# status of a child process.
@@ -539,6 +543,7 @@ sub firstEOLInFile($)
#
# Args:
# $line: the line to parse.
+# $chunkSentinel: the sentinel that surrounds the chunk range information (defaults to "@@").
#
# Returns $chunkRangeHashRef
# $chunkRangeHashRef: a hash reference representing the parts of a chunk range, as follows--
@@ -546,10 +551,11 @@ sub firstEOLInFile($)
# lineCount: the line count in the original file.
# newStartingLine: the new starting line in the new file.
# newLineCount: the new line count in the new file.
-sub parseChunkRange($)
+sub parseChunkRange($;$)
{
- my ($line) = @_;
- my $chunkRangeRegEx = qr#^\@\@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? \@\@#;
+ my ($line, $chunkSentinel) = @_;
+ $chunkSentinel = "@@" if !$chunkSentinel;
+ my $chunkRangeRegEx = qr#^\Q$chunkSentinel\E -(\d+)(,(\d+))? \+(\d+)(,(\d+))? \Q$chunkSentinel\E#;
if ($line !~ /$chunkRangeRegEx/) {
return;
}
@@ -801,18 +807,33 @@ sub parseSvnDiffHeader($$)
"source revision number \"$sourceRevision\".") if ($2 != $sourceRevision);
}
}
- } elsif (s/^\+\+\+ [^\t\n\r]+/+++ $indexPath/) {
+ } elsif (s/^\+\+\+ [^\t\n\r]+/+++ $indexPath/ || $isBinary && /^$/) {
$foundHeaderEnding = 1;
} elsif (/^Cannot display: file marked as a binary type.$/) {
$isBinary = 1;
- $foundHeaderEnding = 1;
+ # SVN 1.7 has an unusual display format for a binary diff. It repeats the first
+ # two lines of the diff header. For example:
+ # Index: test_file.swf
+ # ===================================================================
+ # Cannot display: file marked as a binary type.
+ # svn:mime-type = application/octet-stream
+ # Index: test_file.swf
+ # ===================================================================
+ # --- test_file.swf
+ # +++ test_file.swf
+ #
+ # ...
+ # Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
+ # Therefore, we continue reading the diff header until we either encounter a line
+ # that begins with "+++" (SVN 1.7 or greater) or an empty line (SVN version less
+ # than 1.7).
}
$svnConvertedText .= "$_$eol"; # Also restore end-of-line characters.
$_ = <$fileHandle>; # Not defined if end-of-file reached.
- last if (!defined($_) || /$svnDiffStartRegEx/ || $foundHeaderEnding);
+ last if (!defined($_) || !$isBinary && /$svnDiffStartRegEx/ || $foundHeaderEnding);
}
if (!$foundHeaderEnding) {
@@ -1189,9 +1210,18 @@ sub parseSvnProperty($$)
$_ = <$fileHandle>; # Not defined if end-of-file reached.
+ if (defined($_) && defined(parseChunkRange($_, "##"))) {
+ # FIXME: We should validate the chunk range line that is part of an SVN 1.7
+ # property diff. For now, we ignore this line.
+ $_ = <$fileHandle>;
+ }
+
# The "svn diff" command neither inserts newline characters between property values
# nor between successive properties.
#
+ # As of SVN 1.7, "svn diff" may insert "\ No newline at end of property" after a
+ # property value that doesn't end in a newline.
+ #
# FIXME: We do not support property values that contain tailing newline characters
# as it is difficult to disambiguate these trailing newlines from the empty
# line that precedes the contents of a binary patch.
@@ -1207,6 +1237,7 @@ sub parseSvnProperty($$)
# add error checking to prevent '+', '+', ..., '+' and other invalid combinations.
$propertyValueType = $1;
($propertyValue, $_) = parseSvnPropertyValue($fileHandle, $_);
+ $_ = <$fileHandle> if defined($_) && /$svnPropertyValueNoNewlineRegEx/;
}
if (!$propertyValue) {
@@ -1274,7 +1305,7 @@ sub parseSvnPropertyValue($$)
}
while (<$fileHandle>) {
- if (/^[\r\n]+$/ || /$svnPropertyValueStartRegEx/ || /$svnPropertyStartRegEx/) {
+ if (/^[\r\n]+$/ || /$svnPropertyValueStartRegEx/ || /$svnPropertyStartRegEx/ || /$svnPropertyValueNoNewlineRegEx/) {
# Note, we may encounter an empty line before the contents of a binary patch.
# Also, we check for $svnPropertyValueStartRegEx because a '-' property may be
# followed by a '+' property in the case of a "Modified" or "Name" property.
@@ -2059,4 +2090,55 @@ sub runCommand(@)
exec { $args[0] } @args or die "Failed to exec(): $!";
}
+sub gitCommitForSVNRevision
+{
+ my ($svnRevision) = @_;
+ my $command = "git svn find-rev r" . $svnRevision;
+ $command = "LC_ALL=C $command" if !isWindows();
+ my $gitHash = `$command`;
+ if (!defined($gitHash)) {
+ $gitHash = "unknown";
+ warn "Unable to determine GIT commit from SVN revision";
+ } else {
+ chop($gitHash);
+ }
+ return $gitHash;
+}
+
+sub listOfChangedFilesBetweenRevisions
+{
+ my ($sourceDir, $firstRevision, $lastRevision) = @_;
+ my $command;
+
+ if ($firstRevision eq "unknown" or $lastRevision eq "unknown") {
+ return ();
+ }
+
+ # Some VCS functions don't work from within the build dir, so always
+ # go to the source dir first.
+ my $cwd = Cwd::getcwd();
+ chdir $sourceDir;
+
+ if (isGit()) {
+ my $firstCommit = gitCommitForSVNRevision($firstRevision);
+ my $lastCommit = gitCommitForSVNRevision($lastRevision);
+ $command = "git diff --name-status $firstCommit..$lastCommit";
+ } elsif (isSVN()) {
+ $command = "svn diff --summarize -r $firstRevision:$lastRevision";
+ }
+
+ my @result = ();
+
+ if ($command) {
+ my $diffOutput = `$command`;
+ $diffOutput =~ s/^[A-Z]\s+//gm;
+ @result = split(/[\r\n]+/, $diffOutput);
+ }
+
+ chdir $cwd;
+
+ return @result;
+}
+
+
1;