• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/perl -w
2
3# Copyright (C) 2006, 2007, 2008, 2009 Apple Inc.  All rights reserved.
4# Copyright (C) 2009 Torch Mobile Inc. 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# Script to put change log comments in as default check-in comment.
31
32use strict;
33use File::Basename;
34use File::Spec;
35use FindBin;
36use lib $FindBin::Bin;
37use Term::ReadKey;
38use VCSUtils;
39use webkitdirs;
40
41sub normalizeLineEndings($$);
42
43sub usage
44{
45    print "Usage: [--help] [--regenerate-log] <log file>\n";
46    exit 1;
47}
48
49my $help = checkForArgumentAndRemoveFromARGV("--help");
50if ($help) {
51    usage();
52}
53
54my $regenerateLog = checkForArgumentAndRemoveFromARGV("--regenerate-log");
55my $log = $ARGV[0];
56if (!$log) {
57    usage();
58}
59
60my $baseDir = baseProductDir();
61
62my $editor = $ENV{SVN_LOG_EDITOR};
63if (!$editor) {
64    $editor = $ENV{CVS_LOG_EDITOR};
65}
66if (!$editor) {
67    my $builtEditorApplication = "$baseDir/Release/Commit Log Editor.app/Contents/MacOS/Commit Log Editor";
68    $editor = $builtEditorApplication if -x $builtEditorApplication;
69}
70if (!$editor) {
71    my $builtEditorApplication = "$baseDir/Debug/Commit Log Editor.app/Contents/MacOS/Commit Log Editor";
72    $editor = $builtEditorApplication if -x $builtEditorApplication;
73}
74if (!$editor) {
75    my $installedEditorApplication = "$ENV{HOME}/Applications/Commit Log Editor.app/Contents/MacOS/Commit Log Editor";
76    $editor = $installedEditorApplication if -x $installedEditorApplication;
77}
78if (!$editor) {
79    $editor = $ENV{EDITOR} || "/usr/bin/vi";
80}
81
82my $inChangesToBeCommitted = !isGit();
83my @changeLogs = ();
84my $logContents = "";
85my $existingLog = 0;
86open LOG, $log or die;
87while (<LOG>) {
88    if (isGit()) {
89        if (/^# Changes to be committed:$/) {
90            $inChangesToBeCommitted = 1;
91        } elsif ($inChangesToBeCommitted && /^# \S/) {
92            $inChangesToBeCommitted = 0;
93        }
94    }
95
96    if (!isGit() || /^#/) { #
97        $logContents .= $_;
98    } else {
99        # $_ contains the current git log message
100        # (without the log comment info). We don't need it.
101    }
102    $existingLog = isGit() && !(/^#/ || /^\s*$/) unless $existingLog;
103
104    push @changeLogs, makeFilePathRelative($1) if $inChangesToBeCommitted && (/^M....(.*ChangeLog)\r?\n?$/ || /^#\tmodified:   (.*ChangeLog)/) && !/-ChangeLog/;
105}
106close LOG;
107
108# We want to match the line endings of the existing log file in case they're
109# different from perl's line endings.
110my $endl = "\n";
111$endl = $1 if $logContents =~ /(\r?\n)/;
112
113my $keepExistingLog = 1;
114if ($regenerateLog && $existingLog && scalar(@changeLogs) > 0) {
115    print "Existing log message detected, Use 'r' to regenerate log message from ChangeLogs, or any other key to keep the existing message.\n";
116    ReadMode('cbreak');
117    my $key = ReadKey(0);
118    ReadMode('normal');
119    $keepExistingLog = 0 if ($key eq "r");
120}
121
122# Don't change anything if there's already a log message
123# (as can happen with git-commit --amend)
124exec $editor, @ARGV if $existingLog && $keepExistingLog;
125
126my $topLevel = determineVCSRoot();
127
128my %changeLogSort;
129my %changeLogContents;
130for my $changeLog (@changeLogs) {
131    open CHANGELOG, $changeLog or die "Can't open $changeLog";
132    my $contents = "";
133    my $blankLines = "";
134    my $reviewedByLine = "";
135    my $lineCount = 0;
136    my $date = "";
137    my $author = "";
138    my $email = "";
139    my $hasAuthorInfoToWrite = 0;
140    while (<CHANGELOG>) {
141        if (/^\S/) {
142            last if $contents;
143        }
144        if (/\S/) {
145            my $previousLineWasBlank = 1 unless $blankLines eq "";
146            my $line = $_;
147            my $currentLineBlankLines = $blankLines;
148            $blankLines = "";
149
150            # Remove indentation spaces
151            $line =~ s/^ {8}//;
152
153            # Save the reviewed by line
154            if ($line =~ m/^Reviewed by .*/) {
155                $reviewedByLine = $line;
156                next;
157            }
158
159            # Grab the author and the date line
160            if ($line =~ m/^([0-9]{4}-[0-9]{2}-[0-9]{2})\s+(.*[^\s])\s+<(.*)>/ && $lineCount == 0) {
161                $date = $1;
162                $author = $2;
163                $email = $3;
164                $hasAuthorInfoToWrite = 1;
165                next;
166            }
167
168            $contents .= $currentLineBlankLines if $contents;
169
170            # Attempt to insert the "patch by" line, after the first blank line.
171            if ($previousLineWasBlank && $hasAuthorInfoToWrite && $lineCount > 0) {
172                my $authorAndCommitterAreSamePerson = $ENV{EMAIL_ADDRESS} && $email eq $ENV{EMAIL_ADDRESS};
173                if (!$authorAndCommitterAreSamePerson) {
174                    $contents .= "Patch by $author <$email> on $date\n";
175                    $hasAuthorInfoToWrite = 0;
176                }
177            }
178
179            # Attempt to insert the "reviewed by" line, after the first blank line.
180            if ($previousLineWasBlank && $reviewedByLine && $lineCount > 0) {
181                $contents .= $reviewedByLine . "\n";
182                $reviewedByLine = "";
183            }
184
185
186            $lineCount++;
187            $contents .= $line;
188        } else {
189            $blankLines .= $_;
190        }
191    }
192    if ($reviewedByLine) {
193        $contents .= "\n".$reviewedByLine;
194    }
195    close CHANGELOG;
196
197    $changeLog = File::Spec->abs2rel(File::Spec->rel2abs($changeLog), $topLevel);
198
199    my $label = dirname($changeLog);
200    $label = "top level" unless length $label;
201
202    my $sortKey = lc $label;
203    if ($label eq "top level") {
204        $sortKey = "";
205    } elsif ($label eq "Tools") {
206        $sortKey = "-, just after top level";
207    } elsif ($label eq "WebBrowser") {
208        $sortKey = lc "WebKit, WebBrowser after";
209    } elsif ($label eq "WebCore") {
210        $sortKey = lc "WebFoundation, WebCore after";
211    } elsif ($label eq "LayoutTests") {
212        $sortKey = lc "~, LayoutTests last";
213    }
214
215    $changeLogSort{$sortKey} = $label;
216    $changeLogContents{$label} = $contents;
217}
218
219my $first = 1;
220open NEWLOG, ">$log.edit" or die;
221if (isGit() && scalar keys %changeLogSort == 0) {
222    # populate git commit message with WebKit-format ChangeLog entries unless explicitly disabled
223    my $branch = gitBranch();
224    chomp(my $webkitGenerateCommitMessage = `git config --bool branch.$branch.webkitGenerateCommitMessage`);
225    if ($webkitGenerateCommitMessage eq "") {
226        chomp($webkitGenerateCommitMessage = `git config --bool core.webkitGenerateCommitMessage`);
227    }
228    if ($webkitGenerateCommitMessage ne "false") {
229        open CHANGELOG_ENTRIES, "-|", "prepare-ChangeLog --git-index --no-write" or die "prepare-ChangeLog failed: $!.\n";
230        while (<CHANGELOG_ENTRIES>) {
231            print NEWLOG normalizeLineEndings($_, $endl);
232        }
233        close CHANGELOG_ENTRIES;
234    }
235} else {
236    for my $sortKey (sort keys %changeLogSort) {
237        my $label = $changeLogSort{$sortKey};
238        if (keys %changeLogSort > 1) {
239            print NEWLOG normalizeLineEndings("\n", $endl) if !$first;
240            $first = 0;
241            print NEWLOG normalizeLineEndings("$label: ", $endl);
242        }
243        print NEWLOG normalizeLineEndings($changeLogContents{$label}, $endl);
244    }
245}
246print NEWLOG $logContents;
247close NEWLOG;
248
249system $editor, "$log.edit";
250
251open NEWLOG, "$log.edit" or exit;
252my $foundComment = 0;
253while (<NEWLOG>) {
254    $foundComment = 1 if (/\S/ && !/^CVS:/);
255}
256close NEWLOG;
257
258if ($foundComment) {
259    open NEWLOG, "$log.edit" or die;
260    open LOG, ">$log" or die;
261    while (<NEWLOG>) {
262        print LOG;
263    }
264    close LOG;
265    close NEWLOG;
266}
267
268unlink "$log.edit";
269
270sub normalizeLineEndings($$)
271{
272    my ($string, $endl) = @_;
273    $string =~ s/\r?\n/$endl/g;
274    return $string;
275}
276