summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/resolve-ChangeLogs
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts/resolve-ChangeLogs')
-rwxr-xr-xWebKitTools/Scripts/resolve-ChangeLogs227
1 files changed, 195 insertions, 32 deletions
diff --git a/WebKitTools/Scripts/resolve-ChangeLogs b/WebKitTools/Scripts/resolve-ChangeLogs
index fa01243..58471ec 100755
--- a/WebKitTools/Scripts/resolve-ChangeLogs
+++ b/WebKitTools/Scripts/resolve-ChangeLogs
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
-# Copyright (C) 2007 Apple Inc. All rights reserved.
+# Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -34,73 +34,82 @@ use FindBin;
use lib $FindBin::Bin;
use File::Basename;
+use File::Path;
use File::Spec;
use Getopt::Long;
+use POSIX;
use VCSUtils;
sub conflictFiles($);
+sub findChangeLog($);
sub fixChangeLogPatch($);
+sub fixMergedChangeLogs($;@);
+sub fixOneMergedChangeLog($);
sub mergeChanges($$$);
+sub parseFixMerged($$;$);
+sub removeChangeLogArguments();
+sub resolveChangeLog($);
sub resolveConflict($);
sub showStatus($;$);
+sub usageAndExit();
my $SVN = "svn";
my $GIT = "git";
+my $fixMerged;
my $printWarnings = 1;
my $showHelp;
my $getOptionsResult = GetOptions(
+ 'f|fix-merged:s' => \&parseFixMerged,
'h|help' => \$showHelp,
'w|warnings!' => \$printWarnings,
);
-sub findChangeLog {
- return $_ if basename($_) eq "ChangeLog";
+my @changeLogFiles = removeChangeLogArguments();
- my $file = File::Spec->catfile($_, "ChangeLog");
- return $file if -d $_ and -e $file;
-
- return undef;
-}
-
-my @changeLogFiles = grep { defined $_ } map { findChangeLog($_) } @ARGV;
-
-if (scalar(@changeLogFiles) != scalar(@ARGV)) {
+if (scalar(@ARGV) > 0) {
print STDERR "ERROR: Files listed on command-line that are not ChangeLogs.\n";
undef $getOptionsResult;
-} elsif (scalar(@changeLogFiles) == 0) {
+} elsif (!defined $fixMerged && scalar(@changeLogFiles) == 0) {
print STDERR "ERROR: No ChangeLog files listed on command-line.\n";
undef $getOptionsResult;
+} elsif (defined $fixMerged && !isGit()) {
+ print STDERR "ERROR: --fix-merged may only be used with a git repository\n";
+ undef $getOptionsResult;
}
-if (!$getOptionsResult || $showHelp) {
+sub usageAndExit()
+{
print STDERR <<__END__;
Usage: @{[ basename($0) ]} [options] path/to/ChangeLog [path/to/another/ChangeLog ...]
- -h|--help show this help message
- -w|--[no-]warnings show or suppress warnings (default: show warnings)
+ -f|--fix-merged [revision-range] fix git-merged ChangeLog entries; if a revision-range
+ is specified, run git filter-branch on the range
+ -h|--help show this help message
+ -w|--[no-]warnings show or suppress warnings (default: show warnings)
__END__
exit 1;
}
-for my $file (@changeLogFiles) {
- my ($fileMine, $fileOlder, $fileNewer) = conflictFiles($file);
- if (!$fileMine || !$fileOlder || !$fileNewer) {
- next;
- }
- if (mergeChanges($fileMine, $fileOlder, $fileNewer)) {
- if ($file ne $fileNewer) {
- unlink($file);
- rename($fileNewer, $file) || die;
+if (!$getOptionsResult || $showHelp) {
+ usageAndExit();
+}
+
+if (defined $fixMerged && length($fixMerged) > 0) {
+ my $commitRange = $fixMerged;
+ $commitRange = $commitRange . "..HEAD" if index($commitRange, "..") < 0;
+ fixMergedChangeLogs($commitRange, @changeLogFiles);
+} elsif (@changeLogFiles) {
+ for my $file (@changeLogFiles) {
+ if (defined $fixMerged) {
+ fixOneMergedChangeLog($file);
+ } else {
+ resolveChangeLog($file);
}
- unlink($fileMine, $fileOlder);
- resolveConflict($file);
- showStatus($file, 1);
- } else {
- showStatus($file);
- print STDERR "WARNING: ${file} could not be merged using fuzz level 3.\n" if $printWarnings;
- unlink($fileMine, $fileOlder, $fileNewer) if isGit();
}
+} else {
+ print STDERR "ERROR: Unknown combination of switches and arguments.\n";
+ usageAndExit();
}
exit 0;
@@ -176,6 +185,15 @@ sub conflictFiles($)
return ($fileMine, $fileOlder, $fileNewer);
}
+sub findChangeLog($) {
+ return $_[0] if basename($_[0]) eq "ChangeLog";
+
+ my $file = File::Spec->catfile($_[0], "ChangeLog");
+ return $file if -d $_[0] and -e $file;
+
+ return undef;
+}
+
sub fixChangeLogPatch($)
{
my $patch = shift;
@@ -227,6 +245,96 @@ sub fixChangeLogPatch($)
return $newPatch;
}
+sub fixMergedChangeLogs($;@)
+{
+ my $revisionRange = shift;
+ my @changedFiles = @_;
+
+ if (scalar(@changedFiles) < 1) {
+ # Read in list of files changed in $revisionRange
+ open GIT, "-|", $GIT, "diff", "--name-only", $revisionRange || die;
+ push @changedFiles, <GIT>;
+ close GIT || die;
+ die "No changed files in $revisionRange" if scalar(@changedFiles) < 1;
+ chomp @changedFiles;
+ }
+
+ my @changeLogs = grep { defined $_ } map { findChangeLog($_) } @changedFiles;
+ die "No changed ChangeLog files in $revisionRange" if scalar(@changeLogs) < 1;
+
+ system("$GIT filter-branch --tree-filter 'PREVIOUS_COMMIT=\`$GIT rev-parse \$GIT_COMMIT^\` && MAPPED_PREVIOUS_COMMIT=\`map \$PREVIOUS_COMMIT\` $0 -f \"" . join('" "', @changeLogs) . "\"' $revisionRange");
+
+ # On success, remove the backup refs directory
+ if (WEXITSTATUS($?) == 0) {
+ rmtree(qw(.git/refs/original));
+ }
+}
+
+sub fixOneMergedChangeLog($)
+{
+ my $file = shift;
+ my $patch;
+
+ # Read in patch for incorrectly merged ChangeLog entry
+ {
+ local $/ = undef;
+ open GIT, "-|", $GIT, "diff", ($ENV{GIT_COMMIT} || "HEAD") . "^", $file || die;
+ $patch = <GIT>;
+ close GIT || die;
+ }
+
+ # Always checkout the previous commit's copy of the ChangeLog
+ system($GIT, "checkout", $ENV{MAPPED_PREVIOUS_COMMIT} || "HEAD^", $file);
+
+ # The patch must have 0 or more lines of context, then 1 or more lines
+ # of additions, and then 1 or more lines of context. If not, we skip it.
+ if ($patch =~ /\n@@ -(\d+),(\d+) \+(\d+),(\d+) @@\n( .*\n)*((\+.*\n)+)( .*\n)+$/m) {
+ # Copy the header from the original patch.
+ my $newPatch = substr($patch, 0, index($patch, "@@ -${1},${2} +${3},${4} @@"));
+
+ # Generate a new set of line numbers and patch lengths. Our new
+ # patch will start with the lines for the fixed ChangeLog entry,
+ # then have 3 lines of context from the top of the current file to
+ # make the patch apply cleanly.
+ $newPatch .= "@@ -1,3 +1," . ($4 - $2 + 3) . " @@\n";
+
+ # We assume that top few lines of the ChangeLog entry are actually
+ # at the bottom of the list of added lines (due to the way the patch
+ # algorithm works), so we simply search through the lines until we
+ # find the date line, then move the rest of the lines to the top.
+ my @patchLines = map { $_ . "\n" } split(/\n/, $6);
+ foreach my $i (0 .. $#patchLines) {
+ if ($patchLines[$i] =~ /^\+\d{4}-\d{2}-\d{2} /) {
+ unshift(@patchLines, splice(@patchLines, $i, scalar(@patchLines) - $i));
+ last;
+ }
+ }
+
+ $newPatch .= join("", @patchLines);
+
+ # Add 3 lines of context to the end
+ open FILE, "<", $file || die;
+ for (my $i = 0; $i < 3; $i++) {
+ $newPatch .= " " . <FILE>;
+ }
+ close FILE;
+
+ # Apply the new patch
+ open(PATCH, "| patch -p1 $file > /dev/null") || die;
+ print PATCH $newPatch;
+ close(PATCH) || die;
+
+ # Run "git add" on the fixed ChangeLog file
+ system($GIT, "add", $file);
+
+ showStatus($file, 1);
+ } elsif ($patch) {
+ # Restore the current copy of the ChangeLog file since we can't repatch it
+ system($GIT, "checkout", $ENV{GIT_COMMIT} || "HEAD", $file);
+ print STDERR "WARNING: Last change to ${file} could not be fixed and re-merged.\n" if $printWarnings;
+ }
+}
+
sub mergeChanges($$$)
{
my ($fileMine, $fileOlder, $fileNewer) = @_;
@@ -276,6 +384,61 @@ sub mergeChanges($$$)
return $result;
}
+sub parseFixMerged($$;$)
+{
+ my ($switchName, $key, $value) = @_;
+ if (defined $key) {
+ if (defined findChangeLog($key)) {
+ unshift(@ARGV, $key);
+ $fixMerged = "";
+ } else {
+ $fixMerged = $key;
+ }
+ } else {
+ $fixMerged = "";
+ }
+}
+
+sub removeChangeLogArguments()
+{
+ my @results = ();
+
+ for (my $i = 0; $i < scalar(@ARGV); ) {
+ my $file = findChangeLog($ARGV[$i]);
+ if (defined $file) {
+ splice(@ARGV, $i, 1);
+ push @results, $file;
+ } else {
+ $i++;
+ }
+ }
+
+ return @results;
+}
+
+sub resolveChangeLog($)
+{
+ my ($file) = @_;
+
+ my ($fileMine, $fileOlder, $fileNewer) = conflictFiles($file);
+
+ return unless $fileMine && $fileOlder && $fileNewer;
+
+ if (mergeChanges($fileMine, $fileOlder, $fileNewer)) {
+ if ($file ne $fileNewer) {
+ unlink($file);
+ rename($fileNewer, $file) || die;
+ }
+ unlink($fileMine, $fileOlder);
+ resolveConflict($file);
+ showStatus($file, 1);
+ } else {
+ showStatus($file);
+ print STDERR "WARNING: ${file} could not be merged using fuzz level 3.\n" if $printWarnings;
+ unlink($fileMine, $fileOlder, $fileNewer) if isGit();
+ }
+}
+
sub resolveConflict($)
{
my ($file) = @_;