Subversion Repositories DevTools

Rev

Go to most recent revision | Details | 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)) {
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 = "";
35
 
36
	# Skip whitespace.
37
	while (defined($line) && $line =~ /^\s*$/o) {
38
	    $line = <$fh>;
39
	}
40
	return @result unless defined $line;
41
 
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;
49
 
50
	    # Keep reading until we either get to an Index: line, a property
51
	    # block, an Added/Deleted/Modified lines or the end of file.
52
	    while (defined $line &&
53
		   $line !~ /^.*Index:/o &&
54
		   $line !~ /^.*Added:/o &&
55
		   $line !~ /^.*Deleted:/o &&
56
		   $line !~ /^.*Modified:/o &&
57
		   $line !~ /^.*Copied:/o &&
58
		   $line !~ /^.*Property changes on:/o) {
59
		$line = <$fh>;
60
	    }
61
 
62
	    if (! defined $line) {
63
		# End of file has been reached, return what we have parsed.
64
		return @result;
65
	    }
66
	}
67
 
68
	return () unless
69
	    $line =~ /^.*(Index|Added|Modified|Copied|Deleted): (.*)$/o;
70
	$entry_type = $1;
71
	$filename = $2;
72
	$line = <$fh>;
73
 
74
	# The separator line appears next.
75
	return () unless defined $line && $line =~ /^===================================================================$/o;
76
	$line = <$fh>;
77
 
78
	# Check if the delta represents a binary file.
79
	if ($line =~ /^Cannot display: file marked as a binary type\./o ||
80
	    $line =~ /^\(Binary files differ\)/o) {
81
 
82
	    # If it is a new binary file, there will be some lines before
83
	    # the next Index: line, or end of file.  In other cases, it is
84
	    # impossible to know whether the file is being modified or
85
	    # removed, and what revision it is based off.
86
	    $line = <$fh>;
87
	    my $count = 0;
88
	    while (defined $line && $line !~ /^Index|Added|Modified|Deleted/o) {
89
		$line = <$fh>;
90
		$count++;
91
	    }
92
 
93
	    my $chunk = {};
94
	    $chunk->{filename} = $filename;
95
	    if ($entry_type eq "Index") {
96
		$chunk->{revision} = $count > 0 ? $Codestriker::ADDED_REVISION :
97
		    $Codestriker::PATCH_REVISION;
98
	    } elsif ($entry_type eq "Added") {
99
		$chunk->{revision} = $Codestriker::ADDED_REVISION;
100
	    } elsif ($entry_type eq "Deleted") {
101
		$chunk->{revision} = $Codestriker::REMOVED_REVISION;
102
	    } else {
103
		$chunk->{revision} = $Codestriker::PATCH_REVISION;
104
	    }
105
	    $chunk->{old_linenumber} = -1;
106
	    $chunk->{new_linenumber} = -1;
107
	    $chunk->{binary} = 1;
108
	    $chunk->{text} = "";
109
	    $chunk->{description} = "";
110
	    $chunk->{repmatch} = 1;
111
	    push @result, $chunk;
112
	} else {
113
	    # Try and read the base revision this change is against,
114
	    # while handling new and removed files.
115
	    my $base_revision = -1;
116
	    if ($line =~ /^\-\-\- .*\s.* (\d+)/io) {
117
		$base_revision = $1;
118
	    } elsif ($line !~ /^\-\-\- .*/io) {
119
		return ();
120
	    }
121
 
122
	    # Make sure the +++ line is present next.
123
	    $line = <$fh>;
124
	    return () unless defined $line;
125
	    if ($line !~ /^\+\+\+ .*/io) {
126
		return ();
127
	    }
128
 
129
	    # Now parse the unidiff chunks.
130
	    my @file_diffs = Codestriker::FileParser::UnidiffUtils->
131
		read_unidiff_text($fh, $filename, $base_revision, 1);
132
 
133
	    # If $base_revision is -1, and old_linenumber is 0, then
134
	    # the file is added.  If $base_revision is -1, and
135
	    # new_linenumber is 0, then the file is removed.  Update
136
	    # any chunks to indicate this.
137
	    if ($base_revision == -1) {
138
		for (my $i = 0; $i <= $#file_diffs; $i++) {
139
		    my $delta = $file_diffs[$i];
140
		    if ($delta->{old_linenumber} == 0) {
141
			$delta->{revision} = $Codestriker::ADDED_REVISION;
142
		    } elsif ($delta->{new_linenumber} == 0) {
143
			$delta->{revision} = $Codestriker::REMOVED_REVISION;
144
		    }
145
		}
146
	    }
147
 
148
	    push @result, @file_diffs;
149
 
150
	    # Read the next line.
151
	    $line = <$fh>;
152
	}
153
    }
154
 
155
    # Return the found diff chunks.
156
    return @result;
157
}
158
 
159
1;