Changeset 52646 in webkit


Ignore:
Timestamp:
Dec 29, 2009 11:19:18 PM (14 years ago)
Author:
eric@webkit.org
Message:

2009-12-29 Chris Jerdonek <chris.jerdonek@gmail.com>

Reviewed by David Kilzer.

Fixed a bug in fixChangeLogPatch, made it work correctly in
more circumstances, and added unit tests.

https://bugs.webkit.org/show_bug.cgi?id=32919

  • Scripts/VCSUtils.pm: Rewrote fixChangeLogPatch.


  • Scripts/VCSUtils_unittest.pl: Added. Added 7 unit tests for fixChangeLogPatch.
  • Scripts/test-webkit-perl: Added. Added test harness for unit tests of Perl code.
Location:
trunk/WebKitTools
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebKitTools/ChangeLog

    r52645 r52646  
     12009-12-29  Chris Jerdonek  <chris.jerdonek@gmail.com>
     2
     3        Reviewed by David Kilzer.
     4
     5        Fixed a bug in fixChangeLogPatch, made it work correctly in
     6        more circumstances, and added unit tests.
     7
     8        https://bugs.webkit.org/show_bug.cgi?id=32919
     9
     10        * Scripts/VCSUtils.pm:
     11          Rewrote fixChangeLogPatch.
     12         
     13        * Scripts/VCSUtils_unittest.pl: Added.
     14          Added 7 unit tests for fixChangeLogPatch.
     15
     16        * Scripts/test-webkit-perl: Added.
     17          Added test harness for unit tests of Perl code.
     18
    1192009-12-29  Eric Seidel  <eric@webkit.org>
    220
  • trunk/WebKitTools/Scripts/VCSUtils.pm

    r50863 r52646  
    11# Copyright (C) 2007, 2008, 2009 Apple Inc.  All rights reserved.
     2# Copyright (C) 2009 Chris Jerdonek (chris.jerdonek@gmail.com)
    23#
    34# Redistribution and use in source and binary forms, with or without
     
    357358}
    358359
    359 # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will
    360 # have lines of context at the top of a patch when the existing entry has the same
    361 # date and author as the new entry.  Alter the ChangeLog patch so
    362 # that the added lines ("+") in the patch always start at the beginning of the
    363 # patch and there are no initial lines of context.
     360# If possible, returns a ChangeLog patch equivalent to the given one,
     361# but with the newest ChangeLog entry inserted at the top of the
     362# file -- i.e. no leading context and all lines starting with "+".
     363#
     364# If given a patch string not representable as a patch with the above
     365# properties, it returns the input back unchanged.
     366#
     367# WARNING: This subroutine can return an inequivalent patch string if
     368# both the beginning of the new ChangeLog file matches the beginning
     369# of the source ChangeLog, and the source beginning was modified.
     370# Otherwise, it is guaranteed to return an equivalent patch string,
     371# if it returns.
     372#
     373# Applying this subroutine to ChangeLog patches allows svn-apply to
     374# insert new ChangeLog entries at the top of the ChangeLog file.
     375# svn-apply uses patch with --fuzz=3 to do this. We need to apply
     376# this subroutine because the diff(1) command is greedy when matching
     377# lines. A new ChangeLog entry with the same date and author as the
     378# previous will match and cause the diff to have lines of starting
     379# context.
     380#
     381# This subroutine has unit tests in VCSUtils_unittest.pl.
    364382sub fixChangeLogPatch($)
    365383{
     
    368386    $patch =~ /(\r?\n)/;
    369387    my $lineEnding = $1;
    370     my @patchLines = split(/$lineEnding/, $patch);
    371 
    372     # e.g. 2009-06-03  Eric Seidel  <eric@webkit.org>
    373     my $dateLineRegexpString = '^\+(\d{4}-\d{2}-\d{2})' # Consume the leading '+' and the date.
    374                              . '\s+(.+)\s+' # Consume the name.
    375                              . '<([^<>]+)>$'; # And finally the email address.
    376 
    377     # Figure out where the patch contents start and stop.
    378     my $patchHeaderIndex;
    379     my $firstContentIndex;
    380     my $trailingContextIndex;
    381     my $dateIndex;
    382     my $patchEndIndex = scalar(@patchLines);
    383     for (my $index = 0; $index < @patchLines; ++$index) {
    384         my $line = $patchLines[$index];
    385         if ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@$/) { # e.g. @@ -1,5 +1,18 @@
    386             if ($patchHeaderIndex) {
    387                 $patchEndIndex = $index; # We only bother to fix up the first patch fragment.
    388                 last;
    389             }
    390             $patchHeaderIndex = $index;
    391         }
    392         $firstContentIndex = $index if ($patchHeaderIndex && !$firstContentIndex && $line =~ /^\+[^+]/); # Only match after finding patchHeaderIndex, otherwise we'd match "+++".
    393         $dateIndex = $index if ($line =~ /$dateLineRegexpString/);
    394         $trailingContextIndex = $index if ($firstContentIndex && !$trailingContextIndex && $line =~ /^ /);
    395     }
    396     my $contentLineCount = $trailingContextIndex - $firstContentIndex;
    397     my $trailingContextLineCount = $patchEndIndex - $trailingContextIndex;
    398 
    399     # If we didn't find a date line in the content then this is not a patch we should try and fix.
    400     return $patch if (!$dateIndex);
    401 
    402     # We only need to do anything if the date line is not the first content line.
    403     return $patch if ($dateIndex == $firstContentIndex);
    404 
    405     # Write the new patch.
    406     my $totalNewContentLines = $contentLineCount + $trailingContextLineCount;
    407     $patchLines[$patchHeaderIndex] = "@@ -1,$trailingContextLineCount +1,$totalNewContentLines @@"; # Write a new header.
    408     my @repeatedLines = splice(@patchLines, $dateIndex, $trailingContextIndex - $dateIndex); # The date line and all the content after it that diff saw as repeated.
    409     splice(@patchLines, $firstContentIndex, 0, @repeatedLines); # Move the repeated content to the top.
    410     foreach my $line (@repeatedLines) {
    411         $line =~ s/^\+/ /;
    412     }
    413     splice(@patchLines, $trailingContextIndex, $patchEndIndex, @repeatedLines); # Replace trailing context with the repeated content.
    414     splice(@patchLines, $patchHeaderIndex + 1, $firstContentIndex - $patchHeaderIndex - 1); # Remove any leading context.
    415 
    416     return join($lineEnding, @patchLines) . "\n"; # patch(1) expects an extra trailing newline.
     388    my @lines = split(/$lineEnding/, $patch);
     389
     390    my $i = 0; # We reuse the same index throughout.
     391
     392    # Skip to beginning of first chunk.
     393    for (; $i < @lines; ++$i) {
     394        if (substr($lines[$i], 0, 1) eq "@") {
     395            last;
     396        }
     397    }
     398    my $chunkStartIndex = ++$i;
     399
     400    # Optimization: do not process if new lines already begin the chunk.
     401    if (substr($lines[$i], 0, 1) eq "+") {
     402        return $patch;
     403    }
     404
     405    # Skip to first line of newly added ChangeLog entry.
     406    # For example, +2009-06-03  Eric Seidel  <eric@webkit.org>
     407    my $dateStartRegEx = '^\+(\d{4}-\d{2}-\d{2})' # leading "+" and date
     408                         . '\s+(.+)\s+' # name
     409                         . '<([^<>]+)>$'; # e-mail address
     410
     411    for (; $i < @lines; ++$i) {
     412        my $line = $lines[$i];
     413        my $firstChar = substr($line, 0, 1);
     414        if ($line =~ /$dateStartRegEx/) {
     415            last;
     416        } elsif ($firstChar eq " " or $firstChar eq "+") {
     417            next;
     418        }
     419        return $patch; # Do not change if, for example, "-" or "@" found.
     420    }
     421    if ($i >= @lines) {
     422        return $patch; # Do not change if date not found.
     423    }
     424    my $dateStartIndex = $i;
     425
     426    # Rewrite overlapping lines to lead with " ".
     427    my @overlappingLines = (); # These will include a leading "+".
     428    for (; $i < @lines; ++$i) {
     429        my $line = $lines[$i];
     430        if (substr($line, 0, 1) ne "+") {
     431          last;
     432        }
     433        push(@overlappingLines, $line);
     434        $lines[$i] = " " . substr($line, 1);
     435    }
     436
     437    # Remove excess ending context, if necessary.
     438    my $shouldTrimContext = 1;
     439    for (; $i < @lines; ++$i) {
     440        my $firstChar = substr($lines[$i], 0, 1);
     441        if ($firstChar eq " ") {
     442            next;
     443        } elsif ($firstChar eq "@") {
     444            last;
     445        }
     446        $shouldTrimContext = 0; # For example, if "+" or "-" encountered.
     447        last;
     448    }
     449    my $deletedLineCount = 0;
     450    if ($shouldTrimContext) { # Also occurs if end of file reached.
     451        splice(@lines, $i - @overlappingLines, @overlappingLines);
     452        $deletedLineCount = @overlappingLines;
     453    }
     454
     455    # Work backwards, shifting overlapping lines towards front
     456    # while checking that patch stays equivalent.
     457    for ($i = $dateStartIndex - 1; $i >= $chunkStartIndex; --$i) {
     458        my $line = $lines[$i];
     459        if (substr($line, 0, 1) ne " ") {
     460            next;
     461        }
     462        my $text = substr($line, 1);
     463        my $newLine = pop(@overlappingLines);
     464        if ($text ne substr($newLine, 1)) {
     465            return $patch; # Unexpected difference.
     466        }
     467        $lines[$i] = "+$text";
     468    }
     469
     470    # Finish moving whatever overlapping lines remain, and update
     471    # the initial chunk range.
     472    my $chunkRangeRegEx = '^\@\@ -(\d+),(\d+) \+\d+,(\d+) \@\@$'; # e.g. @@ -2,6 +2,18 @@
     473    if ($lines[$chunkStartIndex - 1] !~ /$chunkRangeRegEx/) {
     474        # FIXME: Handle errors differently from ChangeLog files that
     475        # are okay but should not be altered. That way we can find out
     476        # if improvements to the script ever become necessary.
     477        return $patch; # Error: unexpected patch string format.
     478    }
     479    my $skippedFirstLineCount = $1 - 1;
     480    my $oldSourceLineCount = $2;
     481    my $oldTargetLineCount = $3;
     482
     483    if (@overlappingLines != $skippedFirstLineCount) {
     484        # This can happen, for example, when deliberately inserting
     485        # a new ChangeLog entry earlier in the file.
     486        return $patch;
     487    }
     488    # If @overlappingLines > 0, this is where we make use of the
     489    # assumption that the beginning of the source file was not modified.
     490    splice(@lines, $chunkStartIndex, 0, @overlappingLines);
     491
     492    my $sourceLineCount = $oldSourceLineCount + @overlappingLines - $deletedLineCount;
     493    my $targetLineCount = $oldTargetLineCount + @overlappingLines - $deletedLineCount;
     494    $lines[$chunkStartIndex - 1] = "@@ -1,$sourceLineCount +1,$targetLineCount @@";
     495
     496    return join($lineEnding, @lines) . "\n"; # patch(1) expects an extra trailing newline.
    417497}
    418498
Note: See TracChangeset for help on using the changeset viewer.