Subversion Repositories DevTools

Rev

Rev 1299 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1293 dpurdie 1
###############################################################################
2
# Codestriker: Copyright (c) 2001, 2002, 2003 David Sitsky.
3
# All rights reserved.
4
# sits@users.sourceforge.net
5
#
6
# This program is free software; you can redistribute it and modify it under
7
# the terms of the GPL.
8
 
9
# Parser object for reading Subversion diffs.
10
 
11
package Codestriker::FileParser::SubversionDiff;
12
 
13
use strict;
14
use Codestriker::FileParser::UnidiffUtils;
15
 
16
# Return the array of filenames, revision number, linenumber, whether its
17
# binary or not, and the diff text.
18
# Return () if the file can't be parsed, meaning it is in another format.
19
sub parse ($$$) {
20
    my ($type, $fh, $repository) = @_;
21
 
22
    # Array of results found.
23
    my @result = ();
24
 
25
    my $line = <$fh>;
26
    while (defined($line)) {
1299 dpurdie 27
        # Values associated with the diff.
28
        my $entry_type;
29
        my $revision;
30
        my $filename = "";
31
        my $old_linenumber = -1;
32
        my $new_linenumber = -1;
33
        my $binary = 0;
34
        my $diff = "";
1293 dpurdie 35
 
1299 dpurdie 36
        # Skip whitespace.
37
        while (defined($line) && $line =~ /^\s*$/o) {
38
            $line = <$fh>;
39
        }
40
        return @result unless defined $line;
1293 dpurdie 41
 
1299 dpurdie 42
        # For SVN diffs, the start of the diff block is the Index line.
43
        # For SVN look diffs, the start of the diff block contains the change type.
44
        # Also check for presence of property set blocks.
45
        while ($line =~ /^.*Property changes on: .*$/o) {
46
            $line = <$fh>;
47
            return () unless defined $line &&
48
              $line =~ /^___________________________________________________________________$/o;
1293 dpurdie 49
 
1299 dpurdie 50
            # Keep reading until we either get to the separator line or end of file.
51
            while (defined $line &&
52
                   $line !~ /^===================================================================$/o) {
53
                if ($line =~ /^.*(Index|Added|Modified|Copied|Deleted): (.*)$/o) {
54
                    $entry_type = $1;
55
                    $filename = $2;
56
                }
57
                $line = <$fh>;
58
            }
1293 dpurdie 59
 
1299 dpurdie 60
            if (!defined $line) {
61
                # End of file has been reached, return what we have parsed.
62
                return @result;
63
            }
64
        }
1293 dpurdie 65
 
1299 dpurdie 66
        if ($line =~ /^.*(Index|Added|Modified|Copied|Deleted): (.*)$/o) {
67
            $entry_type = $1;
68
            $filename = $2;
69
            $line = <$fh>;
70
        }
1293 dpurdie 71
 
1299 dpurdie 72
        # The separator line appears next.
73
        return () unless defined $line && $line =~ /^===================================================================$/o;
74
        $line = <$fh>;
1293 dpurdie 75
 
1299 dpurdie 76
        # Check if this is a file entry with no content.  If so, skip it.
77
        next if ! defined $line || $line =~ /^\s*$/o;
1293 dpurdie 78
 
1299 dpurdie 79
        # Check if the delta represents a binary file.
80
        if ($line =~ /^Cannot display: file marked as a binary type\./o ||
81
            $line =~ /^\(Binary files differ\)/o) {
1293 dpurdie 82
 
1299 dpurdie 83
            # If it is a new binary file, there will be some lines before
84
            # the next Index: line, or end of file.  In other cases, it is
85
            # impossible to know whether the file is being modified or
86
            # removed, and what revision it is based off.
87
            $line = <$fh>;
88
            my $count = 0;
89
            while (defined $line && $line !~ /^Index|Added|Modified|Deleted|Property changes on:/o) {
90
                $line = <$fh>;
91
                $count++;
92
            }
1293 dpurdie 93
 
1299 dpurdie 94
            my $chunk = {};
95
            $chunk->{filename} = $filename;
96
            if ($entry_type eq "Index") {
97
                $chunk->{revision} = $count > 0 ? $Codestriker::ADDED_REVISION :
98
                  $Codestriker::PATCH_REVISION;
99
            } elsif ($entry_type eq "Added") {
100
                $chunk->{revision} = $Codestriker::ADDED_REVISION;
101
            } elsif ($entry_type eq "Deleted") {
102
                $chunk->{revision} = $Codestriker::REMOVED_REVISION;
103
            } else {
104
                $chunk->{revision} = $Codestriker::PATCH_REVISION;
105
            }
106
            $chunk->{old_linenumber} = -1;
107
            $chunk->{new_linenumber} = -1;
108
            $chunk->{binary} = 1;
109
            $chunk->{text} = "";
110
            $chunk->{description} = "";
111
            $chunk->{repmatch} = 1;
112
            push @result, $chunk;
113
        } else {
114
            # Try and read the base revision this change is against,
115
            # while handling new and removed files.
116
            my $base_revision = -1;
117
            if ($line =~ /^\-\-\- .*\s.*\(.*?(\d+)\)/io) {
118
                $base_revision = $1;
119
            } elsif ($line !~ /^\-\-\- .*/io) {
120
                # This appears to be a new entry with no data - construct
121
                # an appropriate entry.
122
                my $chunk = {};
123
                $chunk->{filename} = $filename;
124
                $chunk->{revision} = $Codestriker::ADDED_REVISION;
125
                $chunk->{old_linenumber} = -1;
126
                $chunk->{new_linenumber} = -1;
127
                $chunk->{binary} = 1;
128
                $chunk->{text} = "";
129
                $chunk->{description} = "";
130
                $chunk->{repmatch} = 1;
131
                push @result, $chunk;
132
                next;
133
            }
134
 
135
            # Make sure the +++ line is present next.
136
            $line = <$fh>;
137
            return () unless defined $line;
138
            if ($line !~ /^\+\+\+ .*/io) {
139
                return ();
140
            }
141
 
142
            # Now parse the unidiff chunks.
143
            my @file_diffs = Codestriker::FileParser::UnidiffUtils->
144
              read_unidiff_text($fh, $filename, $base_revision, 1);
145
 
146
            # If $base_revision is -1, and old_linenumber is 0, then
147
            # the file is added.  If $base_revision is -1, and
148
            # new_linenumber is 0, then the file is removed.  Update
149
            # any chunks to indicate this.
150
            if ($base_revision == -1) {
151
                for (my $i = 0; $i <= $#file_diffs; $i++) {
152
                    my $delta = $file_diffs[$i];
153
                    if ($delta->{old_linenumber} == 0) {
154
                        $delta->{revision} = $Codestriker::ADDED_REVISION;
155
                    } elsif ($delta->{new_linenumber} == 0) {
156
                        $delta->{revision} = $Codestriker::REMOVED_REVISION;
157
                    }
158
                }
159
            }
160
 
161
            push @result, @file_diffs;
162
 
163
            # Read the next line.
164
            $line = <$fh>;
165
        }
1293 dpurdie 166
    }
167
 
168
    # Return the found diff chunks.
169
    return @result;
170
}
1299 dpurdie 171
 
1293 dpurdie 172
1;