d3cf6ab1af4b941f7456cc6e16e2b4cd203836ba
[WebKit-https.git] / WebKitTools / Scripts / cvs-create-patch
1 #!/usr/bin/perl -w
2
3 # Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
4 # Copyright (C) 2005 Ben La Monica <ben.lamonica@gmail.com> All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # 1.  Redistributions of source code must retain the above copyright
11 #     notice, this list of conditions and the following disclaimer. 
12 # 2.  Redistributions in binary form must reproduce the above copyright
13 #     notice, this list of conditions and the following disclaimer in the
14 #     documentation and/or other materials provided with the distribution. 
15 # 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 #     its contributors may be used to endorse or promote products derived
17 #     from this software without specific prior written permission. 
18 #
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 # Simplified but improved "cvs diff" script for Web Kit Open Source Project, used to make patches.
31
32 # Differences from standard "cvs diff":
33 #
34 #   Always passes "-N" to diff so it will include deleted and added files.
35 #   Always passes "-u" to diff so it will use unified diff format.
36 #   Always passes "-p" to diff so it will try to include function names.
37 #   Other command line options are not supported.
38 #   Works in mixed CVS root situations like the mix of internal and open source CVS repositories
39 #       for Apple's Safari and WebKit team, by doing a separate cvs invocation for each directory.
40 #   Fixes diff output paths so they will work with the "patch" tool.
41 #
42 # Missing feature:
43 #
44 #   Handle binary files (some text form of the binary file).
45
46 use strict;
47 use Cwd;
48 use Getopt::Long;
49 use Time::gmtime;
50 use File::stat;
51 use POSIX qw(:errno_h);
52 use Config;
53
54 my $startDir = getcwd();
55 my $includeUnknowns = "";
56 my %paths;
57
58 GetOptions("include-unknowns", \$includeUnknowns);
59
60 # Create list of paths to diff.
61 if (!@ARGV) {
62     $paths{"."} = 1;
63 } else {
64     for my $file (@ARGV) {
65         die "can't handle absolute paths like \"$file\"\n" if $file =~ m|^/|;
66         die "can't handle empty string path\n" if $file eq "";
67         die "can't handle path with ' in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy)
68
69         my $untouchedFile = $file;
70         
71         # Add a leading and trailing slash to simplify logic below.
72         $file = "/$file/";
73
74         # Remove repeated slashes.
75         $file =~ s|//+|/|g;
76
77         # Remove meaningless sequences involving ".".
78         $file =~ s|/\./|/|g;
79
80         # Remove meaningless sequences involving "..".
81         $file =~ s|/[^./]/\.\./|/|g;
82         $file =~ s|/[^/]+[^./]/\.\./|/|g;
83         $file =~ s|/[^./][^/]+/\.\./|/|g;
84         die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|;
85
86         # Remove the leading and trailing slash.
87         $file =~ s|^/(.*)/$|$1|;
88
89         $paths{$file} = 1;
90     }
91 }
92
93 # Remove any paths that also have a parent listed.
94 for my $path (keys %paths) {
95     my $parent = $path;
96     while ($parent =~ s|/+[^/]+$||) {
97         if ($paths{$parent}) {
98             delete $paths{$path};
99             last;
100         }
101     }
102 }
103
104 sub getDirAndBase
105 {
106     my ($path) = @_;
107     if (-d $path) {
108         $path =~ s|/+$||;
109         return ($path, ".");
110     }
111     return ($1, $2) if $path =~ m|^(.+)/([^/]+)$|;
112     $path !~ m|/| or die "Could not parse path name $path.\n";
113     return (".", $path);
114 }
115
116 # Function to generate a diff.
117 sub diff
118 {
119     my ($path) = @_;
120     my ($dir, $base) = getDirAndBase($path);
121     my $errors = "";
122     chdir $dir or die;
123     open DIFF, "cvs -f diff -Npu '$base' |" or die;
124     my $indexPath;
125     while (<DIFF>) {
126         if (/^Index: (.*)/) {
127             $indexPath = $1;
128             if ($dir ne ".") {
129                 $indexPath = "$dir/$indexPath";
130                 s/Index: .*/Index: $indexPath/;
131             }
132         }
133         if ($indexPath) {
134             # Fix paths on diff, ---, and +++ lines to match preceding Index: line.
135             s/\S+$/$indexPath/ if /^diff/;
136             s/^--- \S+/--- $indexPath/;
137             s/^\+\+\+ \S+/+++ $indexPath/;
138         }
139         if (/^\? (.+)$/) {
140             $errors .= $_;
141             my $unknown = $1;
142             if ($includeUnknowns && $unknown !~ /^\./) {
143                 if (-d $unknown) {
144                     addNewDirectory($dir, $unknown);
145                 } else {
146                     addNewFile($dir, $unknown);
147                 }
148             }
149         } else {
150             print;
151         }
152     }
153     close DIFF;
154     chdir $startDir or die;
155     print STDERR $errors;
156 }
157
158 sub addNewDirectory
159 {
160     my $indexPath = "";
161     my $dir = "";
162     my @files;
163
164     $indexPath = shift;
165     $dir = shift;
166
167     opendir NEWDIR, $dir;
168     @files = readdir(NEWDIR);
169     closedir NEWDIR;
170
171     if (!defined $indexPath || $indexPath eq "") {
172         $indexPath = $dir;
173     } else {
174         $indexPath .= "/$dir";
175     }
176
177     for my $file (@files) {
178         next if $file eq "." || $file eq "..";
179         if (-d "$dir/$file") {
180             addNewDirectory($indexPath, "$dir/$file");
181         } else {
182             addNewFile("$indexPath", "$dir/$file");
183         }
184     }
185 }
186
187 sub addNewFile
188 {
189     my $indexPath = "";
190     my $newFile = "";
191     my @fileContents;
192     my $fileDate = "";
193     my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
194
195     $indexPath = shift;
196     $newFile = shift;
197
198     $fileDate = gmtime(stat($newFile)->mtime);
199
200     @fileContents = `diff -Nup /dev/null $newFile`;
201
202     if ($#fileContents == 0) {
203         return;
204     }
205
206     $indexPath =~ s|[^/]+$|| if defined $indexPath;
207     $indexPath .= $newFile;
208
209     #get rid of the first 2 lines
210     shift @fileContents;
211     shift @fileContents;
212
213     print "Index: $indexPath\n";
214     print "===================================================================\n";
215     print "diff -Npu $indexPath\n";
216     print "--- $indexPath\t1 Jan 1970 00:00:00 -0000\n";
217     print "+++ $indexPath\t" . $fileDate->mday . " " . $months[$fileDate->mon] . " " . ($fileDate->year + 1900) . " ";
218     printf "%02d:%02d:%02d -0000\n", ($fileDate->hour, $fileDate->min,$fileDate->sec);
219     print @fileContents;
220 }
221
222 # Generate the diff for each passed file or directory.
223 for my $path (sort keys %paths) {
224     diff($path);
225 }