1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.scm.git;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class ChangedLinesComputer {
private final Tracker tracker = new Tracker();
private final OutputStream receiver = new OutputStream() {
StringBuilder sb = new StringBuilder();
@Override
public void write(int b) {
sb.append((char) b);
if (b == '\n') {
tracker.parseLine(sb.toString());
sb.setLength(0);
}
}
};
/**
* The OutputStream to pass to JGit's diff command.
*/
OutputStream receiver() {
return receiver;
}
/**
* From a stream of unified diff lines emitted by Git <strong>for a single file</strong>,
* compute the line numbers that should be considered changed.
* Example input:
* <pre>
* diff --git a/lao.txt b/lao.txt
* index 635ef2c..7f050f2 100644
* --- a/lao.txt
* +++ b/lao.txt
* @@ -1,7 +1,6 @@
* -The Way that can be told of is not the eternal Way;
* -The name that can be named is not the eternal name.
* The Nameless is the origin of Heaven and Earth;
* -The Named is the mother of all things.
* +The named is the mother of all things.
* +
* Therefore let there always be non-being,
* so we may see their subtlety,
* And let there always be being,
* @@ -9,3 +8,6 @@ And let there always be being,
* The two are the same,
* But after they are produced,
* they have different names.
* +They both may be called deep and profound.
* +Deeper and more profound,
* +The door of all subtleties!names.
* </pre>
* See also: http://www.gnu.org/software/diffutils/manual/html_node/Example-Unified.html#Example-Unified
*/
Set<Integer> changedLines() {
return tracker.changedLines();
}
private static class Tracker {
private static final Pattern START_LINE_IN_TARGET = Pattern.compile(" \\+(\\d+)");
private final Set<Integer> changedLines = new HashSet<>();
private boolean foundStart = false;
private int lineNumInTarget;
private void parseLine(String line) {
if (line.startsWith("@@ ")) {
Matcher matcher = START_LINE_IN_TARGET.matcher(line);
if (!matcher.find()) {
throw new IllegalStateException("Invalid block header on line " + line);
}
foundStart = true;
lineNumInTarget = Integer.parseInt(matcher.group(1));
} else if (foundStart) {
char firstChar = line.charAt(0);
if (firstChar == ' ') {
lineNumInTarget++;
} else if (firstChar == '+') {
changedLines.add(lineNumInTarget);
lineNumInTarget++;
}
}
}
Set<Integer> changedLines() {
return changedLines;
}
}
}
|