1563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#!/usr/bin/perl -w 2563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 3563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. 4563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 5563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Redistribution and use in source and binary forms, with or without 6563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# modification, are permitted provided that the following conditions 7563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# are met: 8563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 9563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 1. Redistributions of source code must retain the above copyright 10563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# notice, this list of conditions and the following disclaimer. 11563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 2. Redistributions in binary form must reproduce the above copyright 12563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# notice, this list of conditions and the following disclaimer in the 13563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# documentation and/or other materials provided with the distribution. 14563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# its contributors may be used to endorse or promote products derived 16563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# from this software without specific prior written permission. 17563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 18563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 29563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Extended "svn diff" script for WebKit Open Source Project, used to make patches. 30563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 31563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Differences from standard "svn diff": 32563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 33563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Uses the real diff, not svn's built-in diff. 34563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Always passes "-p" to diff so it will try to include function names. 35563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles binary files (encoded as a base64 chunk of text). 36563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Sorts the diffs alphabetically by text files, then binary files. 37563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handles copied and moved files. 38563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 39563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Missing features: 40563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 41563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Handle copied and moved directories. 42563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 43563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse strict; 44563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse warnings; 45563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 46563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Config; 47563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::Basename; 48563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::Spec; 49563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse File::stat; 500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochuse FindBin; 51563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Getopt::Long; 520bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochuse lib $FindBin::Bin; 53563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse MIME::Base64; 54563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse POSIX qw(:errno_h); 55563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkuse Time::gmtime; 560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochuse VCSUtils; 57563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 58563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub binarycmp($$); 59dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blocksub diffOptionsForFile($); 60563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findBaseUrl($); 61563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findMimeType($;$); 62563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findModificationType($); 63563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findSourceFileAndRevision($); 640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochsub generateDiff($$); 65563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub generateFileList($\%); 66dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blocksub hunkHeaderLineRegExForFile($); 67563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub isBinaryMimeType($); 68563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub manufacturePatchForAdditionWithHistory($); 69563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub numericcmp($$); 70563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub outputBinaryContent($); 71563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub patchpathcmp($$); 72563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub pathcmp($$); 73563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub processPaths(\@); 74563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub splitpath($); 75563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub testfilecmp($$); 76563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 77563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark$ENV{'LC_ALL'} = 'C'; 78563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 79563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy $showHelp; 80231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockmy $ignoreChangelogs = 0; 810bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochmy $devNull = File::Spec->devnull(); 82563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 83563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy $result = GetOptions( 84563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark "help" => \$showHelp, 85231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block "ignore-changelogs" => \$ignoreChangelogs 86563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark); 87563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkif (!$result || $showHelp) { 88231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block print STDERR basename($0) . " [-h|--help] [--ignore-changelogs] [svndir1 [svndir2 ...]]\n"; 89563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark exit 1; 90563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 91563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 92563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Sort the diffs for easier reviewing. 93563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy %paths = processPaths(@ARGV); 94563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 95563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Generate a list of files requiring diffs. 96563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkmy %diffFiles; 97563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkfor my $path (keys %paths) { 98563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark generateFileList($path, %diffFiles); 99563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 100563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 1010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochmy $svnRoot = determineSVNRoot(); 1020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochmy $prefix = chdirReturningRelativePath($svnRoot); 1030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 104dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockmy $patchSize = 0; 105dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 106563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Generate the diffs, in a order chosen for easy reviewing. 107563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkfor my $path (sort patchpathcmp values %diffFiles) { 108dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block $patchSize += generateDiff($path, $prefix); 109dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block} 110dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 111dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockif ($patchSize > 20480) { 112dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block print STDERR "WARNING: Patch's size is " . int($patchSize/1024) . " kbytes.\n"; 113dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block print STDERR "Patches 20k or smaller are more likely to be reviewed. Larger patches may sit unreviewed for a long time.\n"; 114563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 115563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 116563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkexit 0; 117563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 118563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Overall sort, considering multiple criteria. 119563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub patchpathcmp($$) 120563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 121563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($a, $b) = @_; 122563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 123563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # All binary files come after all non-binary files. 124563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $result = binarycmp($a, $b); 125563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $result if $result; 126563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 127563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # All test files come after all non-test files. 128563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $result = testfilecmp($a, $b); 129563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $result if $result; 130563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 131563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Final sort is a "smart" sort by directory and file name. 132563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return pathcmp($a, $b); 133563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 134563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 135563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Sort so text files appear before binary files. 136563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub binarycmp($$) 137563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 138563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($fileDataA, $fileDataB) = @_; 139563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $fileDataA->{isBinary} <=> $fileDataB->{isBinary}; 140563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 141563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 142dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blocksub diffOptionsForFile($) 143dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{ 144dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block my ($file) = @_; 145dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 146dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block my $options = "uaNp"; 147dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 148dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block if (my $hunkHeaderLineRegEx = hunkHeaderLineRegExForFile($file)) { 149dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block $options .= "F'$hunkHeaderLineRegEx'"; 150dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block } 151dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 152dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return $options; 153dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block} 154dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 155563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findBaseUrl($) 156563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 157563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($infoPath) = @_; 158563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $baseUrl; 159563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark open INFO, "svn info '$infoPath' |" or die; 160563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (<INFO>) { 161231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block if (/^URL: (.+?)[\r\n]*$/) { 162563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $baseUrl = $1; 163563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 164563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 165563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close INFO; 166563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $baseUrl; 167563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 168563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 169563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findMimeType($;$) 170563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 171563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($file, $revision) = @_; 172563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $args = $revision ? "--revision $revision" : ""; 173563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark open PROPGET, "svn propget svn:mime-type $args '$file' |" or die; 174563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $mimeType = <PROPGET>; 175563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close PROPGET; 1760bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch # svn may output a different EOL sequence than $/, so avoid chomp. 1770bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if ($mimeType) { 1780bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch $mimeType =~ s/[\r\n]+$//g; 1790bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 180563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $mimeType; 181563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 182563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 183563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findModificationType($) 184563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 185563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($stat) = @_; 186563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $fileStat = substr($stat, 0, 1); 187563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $propertyStat = substr($stat, 1, 1); 1880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if ($fileStat eq "A" || $fileStat eq "R") { 189563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $additionWithHistory = substr($stat, 3, 1); 190563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $additionWithHistory eq "+" ? "additionWithHistory" : "addition"; 191563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 192563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return "modification" if ($fileStat eq "M" || $propertyStat eq "M"); 193563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return "deletion" if ($fileStat eq "D"); 194563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return undef; 195563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 196563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 197563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub findSourceFileAndRevision($) 198563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 199563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($file) = @_; 200563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $baseUrl = findBaseUrl("."); 201563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $sourceFile; 202563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $sourceRevision; 203563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark open INFO, "svn info '$file' |" or die; 204563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (<INFO>) { 205231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block if (/^Copied From URL: (.+?)[\r\n]*$/) { 206563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $sourceFile = File::Spec->abs2rel($1, $baseUrl); 207563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } elsif (/^Copied From Rev: ([0-9]+)/) { 208563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $sourceRevision = $1; 209563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 210563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 211563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close INFO; 212563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return ($sourceFile, $sourceRevision); 213563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 214563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 2150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochsub generateDiff($$) 216563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 2170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch my ($fileData, $prefix) = @_; 2180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch my $file = File::Spec->catdir($prefix, $fileData->{path}); 219231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block 220231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block if ($ignoreChangelogs && basename($file) eq "ChangeLog") { 221dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return 0; 222231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block } 223231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block 224dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block my $patch = ""; 225563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($fileData->{modificationType} eq "additionWithHistory") { 226563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark manufacturePatchForAdditionWithHistory($fileData); 227563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 228dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 229dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block my $diffOptions = diffOptionsForFile($file); 230dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block open DIFF, "svn diff --diff-cmd diff -x -$diffOptions '$file' |" or die; 231563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (<DIFF>) { 232563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $patch .= $_; 233563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 234563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close DIFF; 235a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch if (basename($file) eq "ChangeLog") { 236a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch my $changeLogHash = fixChangeLogPatch($patch); 237a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch $patch = $changeLogHash->{patch}; 238a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch } 239dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block print $patch; 240563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($fileData->{isBinary}) { 241563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "\n" if ($patch && $patch =~ m/\n\S+$/m); 242563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark outputBinaryContent($file); 243563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 244dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return length($patch); 245563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 246563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 247563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub generateFileList($\%) 248563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 249563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($statPath, $diffFiles) = @_; 250563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my %testDirectories = map { $_ => 1 } qw(LayoutTests); 251563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark open STAT, "svn stat '$statPath' |" or die; 252563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (my $line = <STAT>) { 2530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch # svn may output a different EOL sequence than $/, so avoid chomp. 2540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch $line =~ s/[\r\n]+$//g; 2550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch my $stat; 2560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch my $path; 257231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block if (isSVNVersion16OrNewer()) { 2580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch $stat = substr($line, 0, 8); 2590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch $path = substr($line, 8); 2600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } else { 2610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch $stat = substr($line, 0, 7); 2620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch $path = substr($line, 7); 2630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 264563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark next if -d $path; 265563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $modificationType = findModificationType($stat); 266563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($modificationType) { 267563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $diffFiles->{$path}->{path} = $path; 268563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $diffFiles->{$path}->{modificationType} = $modificationType; 269563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $diffFiles->{$path}->{isBinary} = isBinaryMimeType($path); 270563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $diffFiles->{$path}->{isTestFile} = exists $testDirectories{(File::Spec->splitdir($path))[0]} ? 1 : 0; 271563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($modificationType eq "additionWithHistory") { 272563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($sourceFile, $sourceRevision) = findSourceFileAndRevision($path); 273563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $diffFiles->{$path}->{sourceFile} = $sourceFile; 274563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $diffFiles->{$path}->{sourceRevision} = $sourceRevision; 275563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 276563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } else { 277563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print STDERR $line, "\n"; 278563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 279563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 280563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close STAT; 281563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 282563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 283dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blocksub hunkHeaderLineRegExForFile($) 284dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block{ 285dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block my ($file) = @_; 286dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 287dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block my $startOfObjCInterfaceRegEx = "@(implementation\\|interface\\|protocol)"; 288dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return "^[-+]\\|$startOfObjCInterfaceRegEx" if $file =~ /\.mm?$/; 289dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return "^$startOfObjCInterfaceRegEx" if $file =~ /^(.*\/)?(mac|objc)\// && $file =~ /\.h$/; 290dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block} 291dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 292563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub isBinaryMimeType($) 293563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 294563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($file) = @_; 295563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $mimeType = findMimeType($file); 296563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 0 if (!$mimeType || substr($mimeType, 0, 5) eq "text/"); 297563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return 1; 298563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 299563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 300563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub manufacturePatchForAdditionWithHistory($) 301563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 302563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($fileData) = @_; 303563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $file = $fileData->{path}; 304563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "Index: ${file}\n"; 305563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "=" x 67, "\n"; 306563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $sourceFile = $fileData->{sourceFile}; 307563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $sourceRevision = $fileData->{sourceRevision}; 308563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "--- ${file}\t(revision ${sourceRevision})\t(from ${sourceFile}:${sourceRevision})\n"; 309563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "+++ ${file}\t(working copy)\n"; 310563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($fileData->{isBinary}) { 311563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "\nCannot display: file marked as a binary type.\n"; 312563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $mimeType = findMimeType($file, $sourceRevision); 313563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "svn:mime-type = ${mimeType}\n\n"; 314563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } else { 3150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch print `svn cat ${sourceFile} | diff -u $devNull - | tail -n +3`; 316563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 317563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 318563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 319563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Sort numeric parts of strings as numbers, other parts as strings. 320563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Makes 1.33 come after 1.3, which is cool. 321563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub numericcmp($$) 322563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 323563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($aa, $bb) = @_; 324563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 325563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @a = split /(\d+)/, $aa; 326563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my @b = split /(\d+)/, $bb; 327563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 328563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Compare one chunk at a time. 329563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Each chunk is either all numeric digits, or all not numeric digits. 330563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (@a && @b) { 331563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $a = shift @a; 332563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $b = shift @b; 333563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 334563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Use numeric comparison if chunks are non-equal numbers. 335563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b; 336563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 337563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Use string comparison if chunks are any other kind of non-equal string. 338563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $a cmp $b if $a ne $b; 339563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 340563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 341563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # One of the two is now empty; compare lengths for result in this case. 342563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return @a <=> @b; 343563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 344563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 345563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub outputBinaryContent($) 346563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 347563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($path) = @_; 348563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Deletion 349563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return if (! -e $path); 350563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Addition or Modification 351563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $buffer; 352563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark open BINARY, $path or die; 353563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark while (read(BINARY, $buffer, 60*57)) { 354563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print encode_base64($buffer); 355563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 356563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark close BINARY; 357563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark print "\n"; 358563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 359563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 360563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Sort first by directory, then by file, so all paths in one directory are grouped 361563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# rather than being interspersed with items from subdirectories. 362563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Use numericcmp to sort directory and filenames to make order logical. 363563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Also include a special case for ChangeLog, which comes first in any directory. 364563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub pathcmp($$) 365563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 366563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($fileDataA, $fileDataB) = @_; 367563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 368563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($dira, $namea) = splitpath($fileDataA->{path}); 369563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($dirb, $nameb) = splitpath($fileDataB->{path}); 370563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 371563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return numericcmp($dira, $dirb) if $dira ne $dirb; 372563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return -1 if $namea eq "ChangeLog" && $nameb ne "ChangeLog"; 373563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return +1 if $namea ne "ChangeLog" && $nameb eq "ChangeLog"; 374563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return numericcmp($namea, $nameb); 375563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 376563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 377563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub processPaths(\@) 378563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 379563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($paths) = @_; 380563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return ("." => 1) if (!@{$paths}); 381563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 382563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my %result = (); 383563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 384563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark for my $file (@{$paths}) { 385563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file); 386563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark die "can't handle empty string path\n" if $file eq ""; 387563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy) 388563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 389563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $untouchedFile = $file; 390563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 391563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $file = canonicalizePath($file); 392563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 393563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|; 394563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 395563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $result{$file} = 1; 396563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 397563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 398563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return ("." => 1) if ($result{"."}); 399563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 400563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark # Remove any paths that also have a parent listed. 401563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark for my $path (keys %result) { 402563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent)) { 403563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark if ($result{$parent}) { 404563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark delete $result{$path}; 405563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark last; 406563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 407563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 408563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark } 409563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 410563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return %result; 411563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 412563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 413563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Break up a path into the directory (with slash) and base name. 414563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub splitpath($) 415563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 416563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($path) = @_; 417563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 418563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $pathSeparator = "/"; 419563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my $dirname = dirname($path) . $pathSeparator; 420563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark $dirname = "" if $dirname eq "." . $pathSeparator; 421563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 422563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return ($dirname, basename($path)); 423563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 424563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark 425563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Sort so source code files appear before test files. 426563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarksub testfilecmp($$) 427563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark{ 428563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark my ($fileDataA, $fileDataB) = @_; 429563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark return $fileDataA->{isTestFile} <=> $fileDataB->{isTestFile}; 430563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark} 4310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 432