commit-queue@webkit.org | 026ee04 | 2018-01-04 07:18:18 +0000 | [diff] [blame] | 1 | #!/usr/bin/env perl |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 2 | |
ddkilzer | b3a42ce | 2007-09-26 02:18:22 +0000 | [diff] [blame] | 3 | # Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. |
eric@webkit.org | 8bf6c2c | 2009-09-10 05:17:46 +0000 | [diff] [blame] | 4 | # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 5 | # Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 6 | # |
| 7 | # Redistribution and use in source and binary forms, with or without |
| 8 | # modification, are permitted provided that the following conditions |
| 9 | # are met: |
| 10 | # |
| 11 | # 1. Redistributions of source code must retain the above copyright |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 12 | # notice, this list of conditions and the following disclaimer. |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 13 | # 2. Redistributions in binary form must reproduce the above copyright |
| 14 | # notice, this list of conditions and the following disclaimer in the |
| 15 | # documentation and/or other materials provided with the distribution. |
mjs@apple.com | 9204733 | 2014-03-15 04:08:27 +0000 | [diff] [blame] | 16 | # 3. Neither the name of Apple Inc. ("Apple") nor the names of |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 17 | # its contributors may be used to endorse or promote products derived |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 18 | # from this software without specific prior written permission. |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 19 | # |
| 20 | # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| 21 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 22 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 23 | # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 24 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 25 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 26 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 27 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 29 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | # "patch" script for WebKit Open Source Project, used to apply patches. |
| 32 | |
| 33 | # Differences from invoking "patch -p0": |
| 34 | # |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 35 | # Handles added files (does a svn add with logic to handle local changes). |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 36 | # Handles added directories (does a svn add). |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 37 | # Handles removed files (does a svn rm with logic to handle local changes). |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 38 | # Handles removed directories--those with no more files or directories left in them |
| 39 | # (does a svn rm). |
darin | 7513839 | 2006-01-31 06:08:51 +0000 | [diff] [blame] | 40 | # Has mode where it will roll back to svn version numbers in the patch file so svn |
| 41 | # can do a 3-way merge. |
| 42 | # Paths from Index: lines are used rather than the paths on the patch lines, which |
| 43 | # makes patches generated by "cvs diff" work (increasingly unimportant since we |
| 44 | # use Subversion now). |
cjerdonek@webkit.org | b19ac5e | 2010-05-13 05:39:36 +0000 | [diff] [blame] | 45 | # ChangeLog patches use --fuzz=3 to prevent rejects. |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 46 | # Handles binary files (requires patches made by svn-create-patch). |
ddkilzer | b88c3b9 | 2007-01-02 04:13:43 +0000 | [diff] [blame] | 47 | # Handles copied and moved files (requires patches made by svn-create-patch). |
ddkilzer | b3a42ce | 2007-09-26 02:18:22 +0000 | [diff] [blame] | 48 | # Handles git-diff patches (without binary changes) created at the top-level directory |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 49 | # |
| 50 | # Missing features: |
| 51 | # |
dbates@webkit.org | 1205b1a | 2010-04-11 19:09:29 +0000 | [diff] [blame] | 52 | # Handle property changes. |
ddkilzer | b88c3b9 | 2007-01-02 04:13:43 +0000 | [diff] [blame] | 53 | # Handle copied and moved directories (would require patches made by svn-create-patch). |
darin | 7513839 | 2006-01-31 06:08:51 +0000 | [diff] [blame] | 54 | # When doing a removal, check that old file matches what's being removed. |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 55 | # Notice a patch that's being applied at the "wrong level" and make it work anyway. |
darin | 7513839 | 2006-01-31 06:08:51 +0000 | [diff] [blame] | 56 | # Do a dry run on the whole patch and don't do anything if part of the patch is |
darin | 43efeff | 2006-06-25 20:01:03 +0000 | [diff] [blame] | 57 | # going to fail (probably too strict unless we exclude ChangeLog). |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 58 | # Handle git-diff patches with binary delta |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 59 | |
| 60 | use strict; |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 61 | use warnings; |
| 62 | |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 63 | use Digest::MD5; |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 64 | use File::Basename; |
commit-queue@webkit.org | 161f92f | 2017-11-09 22:13:16 +0000 | [diff] [blame] | 65 | use File::Copy qw(copy); |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 66 | use File::Spec; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 67 | use Getopt::Long; |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 68 | use MIME::Base64; |
ddkilzer | 4394457 | 2006-07-10 03:46:07 +0000 | [diff] [blame] | 69 | use POSIX qw(strftime); |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 70 | |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 71 | use FindBin; |
| 72 | use lib $FindBin::Bin; |
| 73 | use VCSUtils; |
| 74 | |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 75 | sub addDirectoriesIfNeeded($); |
| 76 | sub applyPatch($$;$); |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 77 | sub checksum($); |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 78 | sub handleBinaryChange($$); |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 79 | sub handleGitBinaryChange($$); |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 80 | sub isDirectoryEmptyForRemoval($); |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 81 | sub patch($); |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 82 | sub removeDirectoriesIfNeeded(); |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 83 | |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 84 | # These should be replaced by an scm class/module: |
| 85 | sub scmKnowsOfFile($); |
| 86 | sub scmCopy($$); |
| 87 | sub scmAdd($); |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 88 | sub scmAddQueued($); |
| 89 | sub scmCommitQueueToggledExecutableBit(); |
| 90 | sub scmCommitQueueAdded(); |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 91 | sub scmRemove($); |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 92 | sub scmToggleExecutableBitQueued($$); |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 93 | |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 94 | my $merge = 0; |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 95 | my $showHelp = 0; |
mitz@apple.com | c68fbd4 | 2008-05-02 17:53:59 +0000 | [diff] [blame] | 96 | my $reviewer; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 97 | my $force = 0; |
dburkart@apple.com | d1d27b1 | 2016-04-18 17:13:04 +0000 | [diff] [blame] | 98 | my $skipChangeLogs = 0; |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 99 | my @scmQueuedFilesToAdd = (); |
| 100 | my %scmQueuedExecutableBits; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 101 | |
| 102 | my $optionParseSuccess = GetOptions( |
| 103 | "merge!" => \$merge, |
| 104 | "help!" => \$showHelp, |
| 105 | "reviewer=s" => \$reviewer, |
dburkart@apple.com | d1d27b1 | 2016-04-18 17:13:04 +0000 | [diff] [blame] | 106 | "force!" => \$force, |
| 107 | "skip-changelogs" => \$skipChangeLogs |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 108 | ); |
| 109 | |
| 110 | if (!$optionParseSuccess || $showHelp) { |
dburkart@apple.com | d1d27b1 | 2016-04-18 17:13:04 +0000 | [diff] [blame] | 111 | print STDERR basename($0) . " [-h|--help] [--force] [-m|--merge] [-r|--reviewer name] [--skip-changelogs] patch1 [patch2 ...]\n"; |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 112 | exit 1; |
| 113 | } |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 114 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 115 | my %removeDirectoryIgnoreList = ( |
| 116 | '.' => 1, |
| 117 | '..' => 1, |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 118 | '.git' => 1, |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 119 | '.svn' => 1, |
| 120 | '_svn' => 1, |
| 121 | ); |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 122 | |
cjerdonek@webkit.org | b19ac5e | 2010-05-13 05:39:36 +0000 | [diff] [blame] | 123 | my $epochTime = time(); # This is used to set the date in ChangeLog files. |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 124 | my $globalExitStatus = 0; |
ddkilzer@apple.com | d29b6db | 2009-09-03 19:07:21 +0000 | [diff] [blame] | 125 | |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 126 | my $repositoryRootPath = determineVCSRoot(); |
eric@webkit.org | efd9acc | 2009-09-01 10:24:36 +0000 | [diff] [blame] | 127 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 128 | my %checkedDirectories; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 129 | |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 130 | # Need to use a typeglob to pass the file handle as a parameter, |
| 131 | # otherwise get a bareword error. |
| 132 | my @diffHashRefs = parsePatch(*ARGV); |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 133 | |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 134 | print "Parsed " . @diffHashRefs . " diffs from patch file(s).\n"; |
ossy@webkit.org | 80b89d8 | 2015-06-01 17:13:26 +0000 | [diff] [blame] | 135 | die "No diff found." unless @diffHashRefs; |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 136 | |
| 137 | my $preparedPatchHash = prepareParsedPatch($force, @diffHashRefs); |
| 138 | |
| 139 | my @copyDiffHashRefs = @{$preparedPatchHash->{copyDiffHashRefs}}; |
| 140 | my @nonCopyDiffHashRefs = @{$preparedPatchHash->{nonCopyDiffHashRefs}}; |
| 141 | my %sourceRevisions = %{$preparedPatchHash->{sourceRevisionHash}}; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 142 | |
| 143 | if ($merge) { |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 144 | die "--merge is currently only supported for SVN" unless isSVN(); |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 145 | # How do we handle Git patches applied to an SVN checkout here? |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 146 | for my $file (sort keys %sourceRevisions) { |
| 147 | my $version = $sourceRevisions{$file}; |
eric@webkit.org | 14eb139 | 2009-10-27 17:38:26 +0000 | [diff] [blame] | 148 | print "Getting version $version of $file\n"; |
slewis@apple.com | 4875b63 | 2011-12-16 01:08:25 +0000 | [diff] [blame] | 149 | my $escapedFile = escapeSubversionPath($file); |
| 150 | system("svn", "update", "-r", $version, $escapedFile) == 0 or die "Failed to run svn update -r $version $escapedFile."; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 151 | } |
| 152 | } |
| 153 | |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 154 | # Handle copied and moved files first since moved files may have their |
| 155 | # source deleted before the move. |
| 156 | for my $copyDiffHashRef (@copyDiffHashRefs) { |
| 157 | my $indexPath = $copyDiffHashRef->{indexPath}; |
| 158 | my $copiedFromPath = $copyDiffHashRef->{copiedFromPath}; |
| 159 | |
| 160 | addDirectoriesIfNeeded(dirname($indexPath)); |
| 161 | scmCopy($copiedFromPath, $indexPath); |
ddkilzer | b88c3b9 | 2007-01-02 04:13:43 +0000 | [diff] [blame] | 162 | } |
| 163 | |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 164 | for my $diffHashRef (@nonCopyDiffHashRefs) { |
| 165 | patch($diffHashRef); |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 166 | } |
| 167 | |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 168 | # For git we need to toggle the executable bit before adding the files |
| 169 | # For SVN is the other way around. |
| 170 | if (isGit()) { |
| 171 | scmCommitQueueToggledExecutableBit(); |
| 172 | scmCommitQueueAdded(); |
| 173 | } elsif (isSVN()) { |
| 174 | scmCommitQueueAdded(); |
| 175 | scmCommitQueueToggledExecutableBit(); |
| 176 | } |
| 177 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 178 | removeDirectoriesIfNeeded(); |
| 179 | |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 180 | exit $globalExitStatus; |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 181 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 182 | sub addDirectoriesIfNeeded($) |
| 183 | { |
| 184 | my ($path) = @_; |
| 185 | my @dirs = File::Spec->splitdir($path); |
| 186 | my $dir = "."; |
| 187 | while (scalar @dirs) { |
| 188 | $dir = File::Spec->catdir($dir, shift @dirs); |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 189 | die "'$dir' exists, but is not a directory" if ( -e $dir && ! -d $dir); |
| 190 | if (isGit()) { |
| 191 | # Git removes a directory once the last file in it is removed. We need |
| 192 | # explicitly check for the existence of each directory along the path |
| 193 | # (and create it if it doesn't) so as to support patches that move all files in |
| 194 | # directory A to A/B. That is, we cannot depend on %checkedDirectories. |
| 195 | mkdir $dir if (! -e $dir); |
| 196 | } elsif (isSVN()) { |
| 197 | next if exists $checkedDirectories{$dir}; |
| 198 | if (! -e $dir) { |
| 199 | mkdir $dir or die "Failed to create required directory '$dir' for path '$path'\n"; |
| 200 | scmAdd($dir) |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 201 | } |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 202 | elsif (-d $dir) { |
| 203 | # SVN prints "svn: warning: 'directory' is already under version control" |
| 204 | # if you try and add a directory which is already in the repository |
| 205 | # So we check first to see if the directory is under version control first. |
| 206 | if (!scmKnowsOfFile($dir)) { |
| 207 | scmAdd($dir); |
| 208 | } |
| 209 | } |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 210 | } |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 211 | $checkedDirectories{$dir} = 1; |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 212 | } |
| 213 | } |
| 214 | |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 215 | # Args: |
| 216 | # $patch: a patch string. |
| 217 | # $pathRelativeToRoot: the path of the file to be patched, relative to the |
| 218 | # repository root. This should normally be the path |
| 219 | # found in the patch's "Index:" line. |
| 220 | # $options: a reference to an array of options to pass to the patch command. |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 221 | sub applyPatch($$;$) |
ddkilzer | 76ffa51 | 2006-06-03 17:59:05 +0000 | [diff] [blame] | 222 | { |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 223 | my ($patch, $pathRelativeToRoot, $options) = @_; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 224 | |
ddkilzer@apple.com | 7308a5a | 2010-01-03 21:25:30 +0000 | [diff] [blame] | 225 | my $optionalArgs = {options => $options, ensureForce => $force}; |
| 226 | |
| 227 | my $exitStatus = runPatchCommand($patch, $repositoryRootPath, $pathRelativeToRoot, $optionalArgs); |
| 228 | |
| 229 | if ($exitStatus) { |
| 230 | $globalExitStatus = $exitStatus; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 231 | } |
ddkilzer | 76ffa51 | 2006-06-03 17:59:05 +0000 | [diff] [blame] | 232 | } |
| 233 | |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 234 | sub checksum($) |
| 235 | { |
| 236 | my $file = shift; |
| 237 | open(FILE, $file) or die "Can't open '$file': $!"; |
| 238 | binmode(FILE); |
| 239 | my $checksum = Digest::MD5->new->addfile(*FILE)->hexdigest(); |
| 240 | close(FILE); |
| 241 | return $checksum; |
| 242 | } |
| 243 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 244 | sub handleBinaryChange($$) |
| 245 | { |
| 246 | my ($fullPath, $contents) = @_; |
eric@webkit.org | 67d2847 | 2009-09-22 09:48:37 +0000 | [diff] [blame] | 247 | # [A-Za-z0-9+/] is the class of allowed base64 characters. |
| 248 | # One or more lines, at most 76 characters in length. |
| 249 | # The last line is allowed to have up to two '=' characters at the end (to signify padding). |
| 250 | if ($contents =~ m#((\n[A-Za-z0-9+/]{76})*\n[A-Za-z0-9+/]{2,74}?[A-Za-z0-9+/=]{2}\n)#) { |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 251 | # Addition or Modification |
eric@webkit.org | 14eb139 | 2009-10-27 17:38:26 +0000 | [diff] [blame] | 252 | open FILE, ">", $fullPath or die "Failed to open $fullPath."; |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 253 | print FILE decode_base64($1); |
| 254 | close FILE; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 255 | if (!scmKnowsOfFile($fullPath)) { |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 256 | # Addition |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 257 | scmAddQueued($fullPath); |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 258 | } |
| 259 | } else { |
| 260 | # Deletion |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 261 | scmRemove($fullPath); |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 262 | } |
| 263 | } |
| 264 | |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 265 | sub handleGitBinaryChange($$) |
| 266 | { |
cjerdonek@webkit.org | 498cdeb | 2010-05-09 07:21:35 +0000 | [diff] [blame] | 267 | my ($fullPath, $diffHashRef) = @_; |
| 268 | |
| 269 | my $contents = $diffHashRef->{svnConvertedText}; |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 270 | |
| 271 | my ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk) = decodeGitBinaryPatch($contents, $fullPath); |
dburkart@apple.com | c8f5223 | 2015-10-21 01:41:52 +0000 | [diff] [blame] | 272 | if (!$binaryChunkType) { |
| 273 | die "$fullPath: unknown git binary patch format"; |
| 274 | } |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 275 | |
cjerdonek@webkit.org | 498cdeb | 2010-05-09 07:21:35 +0000 | [diff] [blame] | 276 | my $isFileAddition = $diffHashRef->{isNew}; |
| 277 | my $isFileDeletion = $diffHashRef->{isDeletion}; |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 278 | |
| 279 | my $originalContents = ""; |
| 280 | if (open FILE, $fullPath) { |
| 281 | die "$fullPath already exists" if $isFileAddition; |
| 282 | |
Hironori.Fujii@sony.com | 7fe4838 | 2019-01-08 01:37:58 +0000 | [diff] [blame] | 283 | binmode(FILE); |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 284 | $originalContents = join("", <FILE>); |
| 285 | close FILE; |
| 286 | } |
commit-queue@webkit.org | bb8e4f1 | 2011-01-07 09:46:42 +0000 | [diff] [blame] | 287 | |
| 288 | if ($reverseBinaryChunkType eq "literal") { |
| 289 | die "Original content of $fullPath mismatches" if $originalContents ne $reverseBinaryChunk; |
| 290 | } |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 291 | |
| 292 | if ($isFileDeletion) { |
| 293 | scmRemove($fullPath); |
| 294 | } else { |
| 295 | # Addition or Modification |
commit-queue@webkit.org | bb8e4f1 | 2011-01-07 09:46:42 +0000 | [diff] [blame] | 296 | my $out = ""; |
| 297 | if ($binaryChunkType eq "delta") { |
| 298 | $out = applyGitBinaryPatchDelta($binaryChunk, $originalContents); |
| 299 | } else { |
| 300 | $out = $binaryChunk; |
| 301 | } |
| 302 | if ($reverseBinaryChunkType eq "delta") { |
| 303 | die "Original content of $fullPath mismatches" if $originalContents ne applyGitBinaryPatchDelta($reverseBinaryChunk, $out); |
| 304 | } |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 305 | open FILE, ">", $fullPath or die "Failed to open $fullPath."; |
Hironori.Fujii@sony.com | 7fe4838 | 2019-01-08 01:37:58 +0000 | [diff] [blame] | 306 | binmode(FILE); |
commit-queue@webkit.org | bb8e4f1 | 2011-01-07 09:46:42 +0000 | [diff] [blame] | 307 | print FILE $out; |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 308 | close FILE; |
| 309 | if ($isFileAddition) { |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 310 | scmAddQueued($fullPath); |
hamaji@chromium.org | 0e18e9c | 2009-11-12 04:19:07 +0000 | [diff] [blame] | 311 | } |
| 312 | } |
| 313 | } |
| 314 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 315 | sub isDirectoryEmptyForRemoval($) |
| 316 | { |
| 317 | my ($dir) = @_; |
eric@webkit.org | 77092af | 2010-04-11 20:19:01 +0000 | [diff] [blame] | 318 | return 1 unless -d $dir; |
ddkilzer | 25b4b50 | 2006-06-25 23:28:14 +0000 | [diff] [blame] | 319 | my $directoryIsEmpty = 1; |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 320 | opendir DIR, $dir or die "Could not open '$dir' to list files: $?"; |
ddkilzer | 25b4b50 | 2006-06-25 23:28:14 +0000 | [diff] [blame] | 321 | for (my $item = readdir DIR; $item && $directoryIsEmpty; $item = readdir DIR) { |
| 322 | next if exists $removeDirectoryIgnoreList{$item}; |
dbates@webkit.org | 7a64a4b | 2010-09-09 21:07:44 +0000 | [diff] [blame] | 323 | if (-d File::Spec->catdir($dir, $item)) { |
ddkilzer | 25b4b50 | 2006-06-25 23:28:14 +0000 | [diff] [blame] | 324 | $directoryIsEmpty = 0; |
ddkilzer | abfe387 | 2006-06-25 19:42:14 +0000 | [diff] [blame] | 325 | } else { |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 326 | next if (scmWillDeleteFile(File::Spec->catdir($dir, $item))); |
ddkilzer | 25b4b50 | 2006-06-25 23:28:14 +0000 | [diff] [blame] | 327 | $directoryIsEmpty = 0; |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 328 | } |
ddkilzer | 25b4b50 | 2006-06-25 23:28:14 +0000 | [diff] [blame] | 329 | } |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 330 | closedir DIR; |
ddkilzer | 25b4b50 | 2006-06-25 23:28:14 +0000 | [diff] [blame] | 331 | return $directoryIsEmpty; |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 332 | } |
| 333 | |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 334 | # Args: |
| 335 | # $diffHashRef: a diff hash reference of the type returned by parsePatch(). |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 336 | sub patch($) |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 337 | { |
cjerdonek@webkit.org | 21256f7 | 2010-04-29 10:33:18 +0000 | [diff] [blame] | 338 | my ($diffHashRef) = @_; |
| 339 | |
cjerdonek@webkit.org | 498cdeb | 2010-05-09 07:21:35 +0000 | [diff] [blame] | 340 | # Make sure $patch is initialized to some value. A deletion can have no |
| 341 | # svnConvertedText property in the case of a deletion resulting from a |
| 342 | # Git rename. |
| 343 | my $patch = $diffHashRef->{svnConvertedText} || ""; |
cjerdonek@webkit.org | 056f91d | 2010-05-07 02:54:06 +0000 | [diff] [blame] | 344 | |
cjerdonek@webkit.org | 507d042 | 2010-05-04 06:14:20 +0000 | [diff] [blame] | 345 | my $fullPath = $diffHashRef->{indexPath}; |
cjerdonek@webkit.org | 056f91d | 2010-05-07 02:54:06 +0000 | [diff] [blame] | 346 | my $isBinary = $diffHashRef->{isBinary}; |
| 347 | my $isGit = $diffHashRef->{isGit}; |
dbates@webkit.org | 4966ccc | 2011-07-19 19:00:58 +0000 | [diff] [blame] | 348 | my $hasTextChunks = $patch && $diffHashRef->{numTextChunks}; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 349 | |
darin | 2e888cc | 2006-02-04 18:51:59 +0000 | [diff] [blame] | 350 | my $deletion = 0; |
| 351 | my $addition = 0; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 352 | |
cjerdonek@webkit.org | a50fb9e | 2010-05-12 05:10:32 +0000 | [diff] [blame] | 353 | $addition = 1 if ($diffHashRef->{isNew} || $patch =~ /\n@@ -0,0 .* @@/); |
cjerdonek@webkit.org | c021653 | 2010-05-09 02:52:26 +0000 | [diff] [blame] | 354 | $deletion = 1 if ($diffHashRef->{isDeletion} || $patch =~ /\n@@ .* \+0,0 @@/); |
darin | 2e888cc | 2006-02-04 18:51:59 +0000 | [diff] [blame] | 355 | |
dburkart@apple.com | d1d27b1 | 2016-04-18 17:13:04 +0000 | [diff] [blame] | 356 | if (basename($fullPath) eq "ChangeLog" && $skipChangeLogs) { |
| 357 | print "Skipping '$fullPath' since --skip-changelogs was passed on the command line."; |
| 358 | return; |
| 359 | } |
| 360 | |
dbates@webkit.org | 4966ccc | 2011-07-19 19:00:58 +0000 | [diff] [blame] | 361 | if (!$addition && !$deletion && !$isBinary && $hasTextChunks) { |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 362 | # Standard patch, patch tool can handle this. |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 363 | if (basename($fullPath) eq "ChangeLog") { |
| 364 | my $changeLogDotOrigExisted = -f "${fullPath}.orig"; |
dbates@webkit.org | 64ce91c | 2010-10-06 04:58:55 +0000 | [diff] [blame] | 365 | my $changeLogHash = fixChangeLogPatch($patch); |
| 366 | my $newPatch = setChangeLogDateAndReviewer($changeLogHash->{patch}, $reviewer, $epochTime); |
cjerdonek@webkit.org | b19ac5e | 2010-05-13 05:39:36 +0000 | [diff] [blame] | 367 | applyPatch($newPatch, $fullPath, ["--fuzz=3"]); |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 368 | unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 369 | } else { |
dbates@webkit.org | bd8724d | 2011-05-15 23:54:51 +0000 | [diff] [blame] | 370 | applyPatch($patch, $fullPath); |
ddkilzer | 76ffa51 | 2006-06-03 17:59:05 +0000 | [diff] [blame] | 371 | } |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 372 | } else { |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 373 | # Either a deletion, an addition or a binary change. |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 374 | |
ddkilzer | 7482950 | 2006-06-19 00:00:53 +0000 | [diff] [blame] | 375 | addDirectoriesIfNeeded(dirname($fullPath)); |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 376 | |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 377 | if ($isBinary) { |
cjerdonek@webkit.org | 056f91d | 2010-05-07 02:54:06 +0000 | [diff] [blame] | 378 | if ($isGit) { |
cjerdonek@webkit.org | 498cdeb | 2010-05-09 07:21:35 +0000 | [diff] [blame] | 379 | handleGitBinaryChange($fullPath, $diffHashRef); |
cjerdonek@webkit.org | 056f91d | 2010-05-07 02:54:06 +0000 | [diff] [blame] | 380 | } else { |
cjerdonek@webkit.org | c021653 | 2010-05-09 02:52:26 +0000 | [diff] [blame] | 381 | handleBinaryChange($fullPath, $patch) if $patch; |
cjerdonek@webkit.org | 056f91d | 2010-05-07 02:54:06 +0000 | [diff] [blame] | 382 | } |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 383 | } elsif ($deletion) { |
ddkilzer@apple.com | 085f42f6 | 2018-05-27 16:50:42 +0000 | [diff] [blame] | 384 | applyPatch($patch, $fullPath, ["--force"]) if ($patch && $hasTextChunks); |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 385 | scmRemove($fullPath); |
dbates@webkit.org | e74c815 | 2014-09-09 20:12:56 +0000 | [diff] [blame] | 386 | } elsif ($addition && $hasTextChunks) { |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 387 | # Addition |
| 388 | rename($fullPath, "$fullPath.orig") if -e $fullPath; |
dbates@webkit.org | e74c815 | 2014-09-09 20:12:56 +0000 | [diff] [blame] | 389 | applyPatch($patch, $fullPath); |
ddkilzer | cd67486 | 2006-07-17 03:59:42 +0000 | [diff] [blame] | 390 | unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 391 | scmAddQueued($fullPath); |
ddkilzer@apple.com | 085f42f6 | 2018-05-27 16:50:42 +0000 | [diff] [blame] | 392 | } elsif ($addition && !$hasTextChunks) { |
| 393 | # Add empty file. |
| 394 | die "\"$fullPath\" already exists" if -e $fullPath; |
| 395 | open(my $FH, ">>", $fullPath) or die "Could not open \"$fullPath\" for writing: $!"; |
| 396 | close($FH); |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 397 | scmAddQueued($fullPath); |
commit-queue@webkit.org | bd272fd | 2019-08-28 03:15:33 +0000 | [diff] [blame] | 398 | } elsif (!defined($diffHashRef->{executableBitDelta})) { |
ddkilzer@apple.com | 085f42f6 | 2018-05-27 16:50:42 +0000 | [diff] [blame] | 399 | die "Can't handle patch for \"$fullPath\"."; |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 400 | } |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 401 | } |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 402 | scmToggleExecutableBitQueued($fullPath, $diffHashRef->{executableBitDelta}) if defined($diffHashRef->{executableBitDelta}); |
thatcher | 021e4c9 | 2006-01-04 21:10:38 +0000 | [diff] [blame] | 403 | } |
| 404 | |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 405 | sub removeDirectoriesIfNeeded() |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 406 | { |
ddkilzer | 576fd48 | 2006-06-25 19:39:22 +0000 | [diff] [blame] | 407 | foreach my $dir (reverse sort keys %checkedDirectories) { |
| 408 | if (isDirectoryEmptyForRemoval($dir)) { |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 409 | scmRemove($dir); |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 410 | } |
ddkilzer | 8816a4e | 2006-06-04 18:52:24 +0000 | [diff] [blame] | 411 | } |
| 412 | } |
| 413 | |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 414 | # This could be made into a more general "status" call, except svn and git |
| 415 | # have different ideas about "moving" files which might get confusing. |
| 416 | sub scmWillDeleteFile($) |
| 417 | { |
| 418 | my ($path) = @_; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 419 | if (isSVN()) { |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 420 | my $svnOutput = svnStatus($path); |
| 421 | return 1 if $svnOutput && substr($svnOutput, 0, 1) eq "D"; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 422 | } elsif (isGit()) { |
dbates@webkit.org | a33baaad | 2012-05-20 23:32:43 +0000 | [diff] [blame] | 423 | my $command = runCommand("git", "diff-index", "--name-status", "HEAD", "--", $path); |
| 424 | return 1 if $command->{stdout} && substr($command->{stdout}, 0, 1) eq "D"; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 425 | } |
| 426 | return 0; |
| 427 | } |
| 428 | |
cjerdonek@webkit.org | 974ec94 | 2010-05-09 02:37:10 +0000 | [diff] [blame] | 429 | # Return whether the file at the given path is known to Git. |
| 430 | # |
| 431 | # This method outputs a message like the following to STDERR when |
| 432 | # returning false: |
| 433 | # |
| 434 | # "error: pathspec 'test.png' did not match any file(s) known to git. |
| 435 | # Did you forget to 'git add'?" |
| 436 | sub gitKnowsOfFile($) |
| 437 | { |
| 438 | my $path = shift; |
| 439 | |
| 440 | `git ls-files --error-unmatch -- $path`; |
cjerdonek@webkit.org | a50fb9e | 2010-05-12 05:10:32 +0000 | [diff] [blame] | 441 | my $exitStatus = exitStatus($?); |
| 442 | return $exitStatus == 0; |
cjerdonek@webkit.org | 974ec94 | 2010-05-09 02:37:10 +0000 | [diff] [blame] | 443 | } |
| 444 | |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 445 | sub scmKnowsOfFile($) |
| 446 | { |
| 447 | my ($path) = @_; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 448 | if (isSVN()) { |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 449 | my $svnOutput = svnStatus($path); |
| 450 | # This will match more than intended. ? might not be the first field in the status |
| 451 | if ($svnOutput && $svnOutput =~ m#\?\s+$path\n#) { |
| 452 | return 0; |
| 453 | } |
| 454 | # This does not handle errors well. |
| 455 | return 1; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 456 | } elsif (isGit()) { |
cjerdonek@webkit.org | 974ec94 | 2010-05-09 02:37:10 +0000 | [diff] [blame] | 457 | my @result = callSilently(\&gitKnowsOfFile, $path); |
| 458 | return $result[0]; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 459 | } |
| 460 | } |
| 461 | |
| 462 | sub scmCopy($$) |
| 463 | { |
| 464 | my ($source, $destination) = @_; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 465 | if (isSVN()) { |
slewis@apple.com | 4875b63 | 2011-12-16 01:08:25 +0000 | [diff] [blame] | 466 | my $escapedSource = escapeSubversionPath($source); |
| 467 | my $escapedDestination = escapeSubversionPath($destination); |
| 468 | system("svn", "copy", $escapedSource, $escapedDestination) == 0 or die "Failed to svn copy $escapedSource $escapedDestination."; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 469 | } elsif (isGit()) { |
commit-queue@webkit.org | 161f92f | 2017-11-09 22:13:16 +0000 | [diff] [blame] | 470 | copy($source, $destination) or die "Failed to copy $source $destination."; |
clopez@igalia.com | e797692 | 2020-07-07 00:46:03 +0000 | [diff] [blame] | 471 | system("git", "add", "-v", $destination) == 0 or die "Failed to git add $destination."; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 472 | } |
| 473 | } |
| 474 | |
| 475 | sub scmAdd($) |
| 476 | { |
| 477 | my ($path) = @_; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 478 | if (isSVN()) { |
slewis@apple.com | 4875b63 | 2011-12-16 01:08:25 +0000 | [diff] [blame] | 479 | my $escapedPath = escapeSubversionPath($path); |
| 480 | system("svn", "add", $escapedPath) == 0 or die "Failed to svn add $escapedPath."; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 481 | } elsif (isGit()) { |
clopez@igalia.com | e797692 | 2020-07-07 00:46:03 +0000 | [diff] [blame] | 482 | system("git", "add", "-v", $path) == 0 or die "Failed to git add $path."; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 483 | } |
| 484 | } |
| 485 | |
| 486 | sub scmRemove($) |
| 487 | { |
| 488 | my ($path) = @_; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 489 | if (isSVN()) { |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 490 | # SVN is very verbose when removing directories. Squelch all output except the last line. |
| 491 | my $svnOutput; |
slewis@apple.com | 4875b63 | 2011-12-16 01:08:25 +0000 | [diff] [blame] | 492 | my $escapedPath = escapeSubversionPath($path); |
| 493 | open SVN, "svn rm --force '$escapedPath' |" or die "svn rm --force '$escapedPath' failed!"; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 494 | # Only print the last line. Subversion outputs all changed statuses below $dir |
| 495 | while (<SVN>) { |
| 496 | $svnOutput = $_; |
| 497 | } |
| 498 | close SVN; |
| 499 | print $svnOutput if $svnOutput; |
ddkilzer@apple.com | 33e4fbc | 2009-09-26 22:57:25 +0000 | [diff] [blame] | 500 | } elsif (isGit()) { |
eric@webkit.org | 77092af | 2010-04-11 20:19:01 +0000 | [diff] [blame] | 501 | # Git removes a directory if it becomes empty when the last file it contains is |
| 502 | # removed by `git rm`. In svn-apply this can happen when a directory is being |
| 503 | # removed in a patch, and all of the files inside of the directory are removed |
| 504 | # before attemping to remove the directory itself. In this case, Git will have |
| 505 | # already deleted the directory and `git rm` would exit with an error claiming |
| 506 | # there was no file. The --ignore-unmatch switch gracefully handles this case. |
| 507 | system("git", "rm", "--force", "--ignore-unmatch", $path) == 0 or die "Failed to git rm --force --ignore-unmatch $path."; |
eric@webkit.org | f135b1c | 2009-06-25 07:45:30 +0000 | [diff] [blame] | 508 | } |
| 509 | } |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 510 | |
| 511 | # Calling "git add" / "svn add" per each file its very slow on big patches |
| 512 | # We queue the filenames to execute them at the end in a few commands. |
| 513 | sub scmAddQueued($) |
| 514 | { |
| 515 | my ($path) = @_; |
| 516 | if (isSVN()) { |
| 517 | push(@scmQueuedFilesToAdd, escapeSubversionPath($path)); |
| 518 | } elsif (isGit()) { |
| 519 | push(@scmQueuedFilesToAdd, $path); |
| 520 | } |
| 521 | } |
| 522 | |
| 523 | sub scmCommitQueueAdded() |
| 524 | { |
| 525 | my @cmdBase; |
| 526 | if (isSVN()) { |
| 527 | @cmdBase = ("svn", "add") |
| 528 | } elsif (isGit()) { |
clopez@igalia.com | e797692 | 2020-07-07 00:46:03 +0000 | [diff] [blame] | 529 | @cmdBase = ("git", "add", "-v") |
clopez@igalia.com | 031f1a2 | 2020-06-04 19:44:30 +0000 | [diff] [blame] | 530 | } |
| 531 | |
| 532 | # When we are handling a very large patch (more than 1000 files modified) |
| 533 | # Instead of executing only one add command, we execute several commands |
| 534 | # with 1000 files each one. We do that to avoid running into E2BIG errors |
| 535 | # on the execve() syscall (argument list is too long). |
| 536 | while (@scmQueuedFilesToAdd) { |
| 537 | my @cmdGrouped = splice(@scmQueuedFilesToAdd, 0, 1000); |
| 538 | unshift (@cmdGrouped, @cmdBase); |
| 539 | system (@cmdGrouped); |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | sub scmToggleExecutableBitQueued($$) |
| 544 | { |
| 545 | my ($path, $executableBitDelta) = @_; |
| 546 | $scmQueuedExecutableBits{$path} = $executableBitDelta; |
| 547 | } |
| 548 | |
| 549 | sub scmCommitQueueToggledExecutableBit() |
| 550 | { |
| 551 | foreach my $path (keys %scmQueuedExecutableBits) { |
| 552 | scmToggleExecutableBit($path, $scmQueuedExecutableBits{$path}); |
| 553 | } |
| 554 | } |