summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/svn-apply
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts/svn-apply')
-rwxr-xr-xWebKitTools/Scripts/svn-apply199
1 files changed, 83 insertions, 116 deletions
diff --git a/WebKitTools/Scripts/svn-apply b/WebKitTools/Scripts/svn-apply
index f586211..33b2279 100755
--- a/WebKitTools/Scripts/svn-apply
+++ b/WebKitTools/Scripts/svn-apply
@@ -42,8 +42,7 @@
# 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, and the entry date is set in
-# the patch to today's date using $changeLogTimeZone.
+# ChangeLog patches use --fuzz=3 to prevent rejects.
# Handles binary files (requires patches made by svn-create-patch).
# Handles copied and moved files (requires patches made by svn-create-patch).
# Handles git-diff patches (without binary changes) created at the top-level directory
@@ -80,7 +79,6 @@ sub handleGitBinaryChange($$);
sub isDirectoryEmptyForRemoval($);
sub patch($);
sub removeDirectoriesIfNeeded();
-sub setChangeLogDateAndReviewer($$);
# These should be replaced by an scm class/module:
sub scmKnowsOfFile($);
@@ -88,10 +86,6 @@ sub scmCopy($$);
sub scmAdd($);
sub scmRemove($);
-
-# Project time zone for Cupertino, CA, US
-my $changeLogTimeZone = "PST8PDT";
-
my $merge = 0;
my $showHelp = 0;
my $reviewer;
@@ -117,78 +111,47 @@ my %removeDirectoryIgnoreList = (
'_svn' => 1,
);
+my $epochTime = time(); # This is used to set the date in ChangeLog files.
my $globalExitStatus = 0;
my $repositoryRootPath = determineVCSRoot();
my %checkedDirectories;
-my %copiedFiles;
-my @patches;
-my %versions;
-
-my $copiedFromPath;
-my $filter;
-my $indexPath;
-my $patch;
-while (<>) {
- s/([\n\r]+)$//mg;
- my $eol = $1;
- if (!defined($indexPath) && m#^diff --git \w/#) {
- $filter = \&gitdiff2svndiff;
- }
- $_ = &$filter($_) if $filter;
- if (/^Index: (.+)/) {
- $indexPath = $1;
- if ($patch) {
- if (!$copiedFromPath) {
- push @patches, $patch;
- }
- $copiedFromPath = "";
- $patch = "";
- }
- }
- if ($indexPath) {
- # Fix paths on diff, ---, and +++ lines to match preceding Index: line.
- s/\S+$/$indexPath/ if /^diff/;
- s/^--- \S+/--- $indexPath/;
- if (/^--- .+\(from (\S+):(\d+)\)$/) {
- $copiedFromPath = $1;
- $copiedFiles{$indexPath} = $copiedFromPath;
- $versions{$copiedFromPath} = $2 if ($2 != 0);
- }
- elsif (/^--- .+\(revision (\d+)\)$/) {
- $versions{$indexPath} = $1 if ($1 != 0);
- }
- if (s/^\+\+\+ \S+/+++ $indexPath/) {
- $indexPath = "";
- }
- }
- $patch .= $_;
- $patch .= $eol;
-}
-if ($patch && !$copiedFromPath) {
- push @patches, $patch;
-}
+# Need to use a typeglob to pass the file handle as a parameter,
+# otherwise get a bareword error.
+my @diffHashRefs = parsePatch(*ARGV);
+
+print "Parsed " . @diffHashRefs . " diffs from patch file(s).\n";
+
+my $preparedPatchHash = prepareParsedPatch($force, @diffHashRefs);
+
+my @copyDiffHashRefs = @{$preparedPatchHash->{copyDiffHashRefs}};
+my @nonCopyDiffHashRefs = @{$preparedPatchHash->{nonCopyDiffHashRefs}};
+my %sourceRevisions = %{$preparedPatchHash->{sourceRevisionHash}};
if ($merge) {
die "--merge is currently only supported for SVN" unless isSVN();
# How do we handle Git patches applied to an SVN checkout here?
- for my $file (sort keys %versions) {
- my $version = $versions{$file};
+ for my $file (sort keys %sourceRevisions) {
+ my $version = $sourceRevisions{$file};
print "Getting version $version of $file\n";
system("svn", "update", "-r", $version, $file) == 0 or die "Failed to run svn update -r $version $file.";
}
}
-# Handle copied and moved files first since moved files may have their source deleted before the move.
-for my $file (keys %copiedFiles) {
- addDirectoriesIfNeeded(dirname($file));
- scmCopy($copiedFiles{$file}, $file);
+# Handle copied and moved files first since moved files may have their
+# source deleted before the move.
+for my $copyDiffHashRef (@copyDiffHashRefs) {
+ my $indexPath = $copyDiffHashRef->{indexPath};
+ my $copiedFromPath = $copyDiffHashRef->{copiedFromPath};
+
+ addDirectoriesIfNeeded(dirname($indexPath));
+ scmCopy($copiedFromPath, $indexPath);
}
-for $patch (@patches) {
- patch($patch);
+for my $diffHashRef (@nonCopyDiffHashRefs) {
+ patch($diffHashRef);
}
removeDirectoriesIfNeeded();
@@ -276,14 +239,16 @@ sub handleBinaryChange($$)
sub handleGitBinaryChange($$)
{
- my ($fullPath, $contents) = @_;
+ my ($fullPath, $diffHashRef) = @_;
+
+ my $contents = $diffHashRef->{svnConvertedText};
my ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk) = decodeGitBinaryPatch($contents, $fullPath);
# FIXME: support "delta" type.
die "only literal type is supported now" if ($binaryChunkType ne "literal" || $reverseBinaryChunkType ne "literal");
- my $isFileAddition = $contents =~ /\nnew file mode \d+\n/;
- my $isFileDeletion = $contents =~ /\ndeleted file mode \d+\n/;
+ my $isFileAddition = $diffHashRef->{isNew};
+ my $isFileDeletion = $diffHashRef->{isDeletion};
my $originalContents = "";
if (open FILE, $fullPath) {
@@ -310,6 +275,7 @@ sub handleGitBinaryChange($$)
sub isDirectoryEmptyForRemoval($)
{
my ($dir) = @_;
+ return 1 unless -d $dir;
my $directoryIsEmpty = 1;
opendir DIR, $dir or die "Could not open '$dir' to list files: $?";
for (my $item = readdir DIR; $item && $directoryIsEmpty; $item = readdir DIR) {
@@ -325,37 +291,36 @@ sub isDirectoryEmptyForRemoval($)
return $directoryIsEmpty;
}
+# Args:
+# $diffHashRef: a diff hash reference of the type returned by parsePatch().
sub patch($)
{
- my ($patch) = @_;
- return if !$patch;
-
- unless ($patch =~ m|^Index: ([^\r\n]+)|) {
- my $separator = '-' x 67;
- warn "Failed to find 'Index:' in:\n$separator\n$patch\n$separator\n";
- die unless $force;
- return;
- }
- my $fullPath = $1;
+ my ($diffHashRef) = @_;
+
+ # Make sure $patch is initialized to some value. A deletion can have no
+ # svnConvertedText property in the case of a deletion resulting from a
+ # Git rename.
+ my $patch = $diffHashRef->{svnConvertedText} || "";
+
+ my $fullPath = $diffHashRef->{indexPath};
+ my $isBinary = $diffHashRef->{isBinary};
+ my $isGit = $diffHashRef->{isGit};
my $deletion = 0;
my $addition = 0;
- my $isBinary = 0;
- my $isGitBinary = 0;
- $addition = 1 if ($patch =~ /\n--- .+\(revision 0\)\r?\n/ || $patch =~ /\n@@ -0,0 .* @@/) && !exists($copiedFiles{$fullPath});
- $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/;
- $isBinary = 1 if $patch =~ /\nCannot display: file marked as a binary type\./;
- $isGitBinary = 1 if $patch =~ /\nGIT binary patch\n/;
+ $addition = 1 if ($diffHashRef->{isNew} || $patch =~ /\n@@ -0,0 .* @@/);
+ $deletion = 1 if ($diffHashRef->{isDeletion} || $patch =~ /\n@@ .* \+0,0 @@/);
- if (!$addition && !$deletion && !$isBinary && !$isGitBinary) {
+ if (!$addition && !$deletion && !$isBinary) {
# Standard patch, patch tool can handle this.
if (basename($fullPath) eq "ChangeLog") {
my $changeLogDotOrigExisted = -f "${fullPath}.orig";
- applyPatch(setChangeLogDateAndReviewer(fixChangeLogPatch($patch), $reviewer), $fullPath, ["--fuzz=3"]);
+ my $newPatch = setChangeLogDateAndReviewer(fixChangeLogPatch($patch), $reviewer, $epochTime);
+ applyPatch($newPatch, $fullPath, ["--fuzz=3"]);
unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted);
} else {
- applyPatch($patch, $fullPath);
+ applyPatch($patch, $fullPath) if $patch;
}
} else {
# Either a deletion, an addition or a binary change.
@@ -363,25 +328,26 @@ sub patch($)
addDirectoriesIfNeeded(dirname($fullPath));
if ($isBinary) {
- # Binary change
- handleBinaryChange($fullPath, $patch);
- } elsif ($isGitBinary) {
- # Git binary change
- handleGitBinaryChange($fullPath, $patch);
+ if ($isGit) {
+ handleGitBinaryChange($fullPath, $diffHashRef);
+ } else {
+ handleBinaryChange($fullPath, $patch) if $patch;
+ }
} elsif ($deletion) {
- # Deletion
- applyPatch($patch, $fullPath, ["--force"]);
+ applyPatch($patch, $fullPath, ["--force"]) if $patch;
scmRemove($fullPath);
} else {
# Addition
rename($fullPath, "$fullPath.orig") if -e $fullPath;
- applyPatch($patch, $fullPath);
+ applyPatch($patch, $fullPath) if $patch;
unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig");
scmAdd($fullPath);
# What is this for?
system("svn", "stat", "$fullPath.orig") if isSVN() && -e "$fullPath.orig";
}
}
+
+ scmToggleExecutableBit($fullPath, $diffHashRef->{executableBitDelta}) if defined($diffHashRef->{executableBitDelta});
}
sub removeDirectoriesIfNeeded()
@@ -393,26 +359,6 @@ sub removeDirectoriesIfNeeded()
}
}
-sub setChangeLogDateAndReviewer($$)
-{
- my $patch = shift;
- my $reviewer = shift;
- my $savedTimeZone = $ENV{'TZ'};
- # Set TZ temporarily so that localtime() is in that time zone
- $ENV{'TZ'} = $changeLogTimeZone;
- my $newDate = strftime("%Y-%m-%d", localtime());
- if (defined $savedTimeZone) {
- $ENV{'TZ'} = $savedTimeZone;
- } else {
- delete $ENV{'TZ'};
- }
- $patch =~ s/(\n\+)\d{4}-[^-]{2}-[^-]{2}( )/$1$newDate$2/;
- if (defined($reviewer)) {
- $patch =~ s/NOBODY \(OOPS!\)/$reviewer/;
- }
- return $patch;
-}
-
# This could be made into a more general "status" call, except svn and git
# have different ideas about "moving" files which might get confusing.
sub scmWillDeleteFile($)
@@ -428,6 +374,22 @@ sub scmWillDeleteFile($)
return 0;
}
+# Return whether the file at the given path is known to Git.
+#
+# This method outputs a message like the following to STDERR when
+# returning false:
+#
+# "error: pathspec 'test.png' did not match any file(s) known to git.
+# Did you forget to 'git add'?"
+sub gitKnowsOfFile($)
+{
+ my $path = shift;
+
+ `git ls-files --error-unmatch -- $path`;
+ my $exitStatus = exitStatus($?);
+ return $exitStatus == 0;
+}
+
sub scmKnowsOfFile($)
{
my ($path) = @_;
@@ -440,9 +402,8 @@ sub scmKnowsOfFile($)
# This does not handle errors well.
return 1;
} elsif (isGit()) {
- `git ls-files --error-unmatch -- $path`;
- my $exitCode = $? >> 8;
- return $exitCode == 0;
+ my @result = callSilently(\&gitKnowsOfFile, $path);
+ return $result[0];
}
}
@@ -481,6 +442,12 @@ sub scmRemove($)
close SVN;
print $svnOutput if $svnOutput;
} elsif (isGit()) {
- system("git", "rm", "--force", $path) == 0 or die "Failed to git rm --force $path.";
+ # Git removes a directory if it becomes empty when the last file it contains is
+ # removed by `git rm`. In svn-apply this can happen when a directory is being
+ # removed in a patch, and all of the files inside of the directory are removed
+ # before attemping to remove the directory itself. In this case, Git will have
+ # already deleted the directory and `git rm` would exit with an error claiming
+ # there was no file. The --ignore-unmatch switch gracefully handles this case.
+ system("git", "rm", "--force", "--ignore-unmatch", $path) == 0 or die "Failed to git rm --force --ignore-unmatch $path.";
}
}