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 package org.eclipse.jgit.api;
44
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.io.FileNotFoundException;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.util.ArrayList;
51 import java.util.Collection;
52 import java.util.Collections;
53
54 import org.eclipse.jgit.api.errors.GitAPIException;
55 import org.eclipse.jgit.api.errors.JGitInternalException;
56 import org.eclipse.jgit.blame.BlameGenerator;
57 import org.eclipse.jgit.blame.BlameResult;
58 import org.eclipse.jgit.diff.DiffAlgorithm;
59 import org.eclipse.jgit.diff.RawText;
60 import org.eclipse.jgit.diff.RawTextComparator;
61 import org.eclipse.jgit.dircache.DirCache;
62 import org.eclipse.jgit.lib.AnyObjectId;
63 import org.eclipse.jgit.lib.Constants;
64 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.Repository;
67 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
68 import org.eclipse.jgit.util.IO;
69 import org.eclipse.jgit.util.io.AutoLFInputStream;
70
71
72
73
74 public class BlameCommand extends GitCommand<BlameResult> {
75
76 private String path;
77
78 private DiffAlgorithm diffAlgorithm;
79
80 private RawTextComparator textComparator;
81
82 private ObjectId startCommit;
83
84 private Collection<ObjectId> reverseEndCommits;
85
86 private Boolean followFileRenames;
87
88
89
90
91 public BlameCommand(Repository repo) {
92 super(repo);
93 }
94
95
96
97
98
99
100
101
102 public BlameCommand setFilePath(String filePath) {
103 this.path = filePath;
104 return this;
105 }
106
107
108
109
110
111
112
113 public BlameCommand setDiffAlgorithm(DiffAlgorithm diffAlgorithm) {
114 this.diffAlgorithm = diffAlgorithm;
115 return this;
116 }
117
118
119
120
121
122
123
124 public BlameCommand setTextComparator(RawTextComparator textComparator) {
125 this.textComparator = textComparator;
126 return this;
127 }
128
129
130
131
132
133
134
135 public BlameCommand setStartCommit(AnyObjectId commit) {
136 this.startCommit = commit.toObjectId();
137 return this;
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152 public BlameCommand setFollowFileRenames(boolean follow) {
153 followFileRenames = Boolean.valueOf(follow);
154 return this;
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170 public BlameCommand reverse(AnyObjectId start, AnyObjectId end)
171 throws IOException {
172 return reverse(start, Collections.singleton(end.toObjectId()));
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 public BlameCommand reverse(AnyObjectId start, Collection<ObjectId> end)
189 throws IOException {
190 startCommit = start.toObjectId();
191 reverseEndCommits = new ArrayList<>(end);
192 return this;
193 }
194
195
196
197
198
199
200
201 @Override
202 public BlameResult call() throws GitAPIException {
203 checkCallable();
204 try (BlameGenerator gen = new BlameGenerator(repo, path)) {
205 if (diffAlgorithm != null)
206 gen.setDiffAlgorithm(diffAlgorithm);
207 if (textComparator != null)
208 gen.setTextComparator(textComparator);
209 if (followFileRenames != null)
210 gen.setFollowFileRenames(followFileRenames.booleanValue());
211
212 if (reverseEndCommits != null)
213 gen.reverse(startCommit, reverseEndCommits);
214 else if (startCommit != null)
215 gen.push(null, startCommit);
216 else {
217 gen.push(null, repo.resolve(Constants.HEAD));
218 if (!repo.isBare()) {
219 DirCache dc = repo.readDirCache();
220 int entry = dc.findEntry(path);
221 if (0 <= entry)
222 gen.push(null, dc.getEntry(entry).getObjectId());
223
224 File inTree = new File(repo.getWorkTree(), path);
225 if (repo.getFS().isFile(inTree)) {
226 RawText rawText = getRawText(inTree);
227 gen.push(null, rawText);
228 }
229 }
230 }
231 return gen.computeBlameResult();
232 } catch (IOException e) {
233 throw new JGitInternalException(e.getMessage(), e);
234 }
235 }
236
237 private RawText getRawText(File inTree) throws IOException,
238 FileNotFoundException {
239 RawText rawText;
240
241 WorkingTreeOptions workingTreeOptions = getRepository().getConfig()
242 .get(WorkingTreeOptions.KEY);
243 AutoCRLF autoCRLF = workingTreeOptions.getAutoCRLF();
244 switch (autoCRLF) {
245 case FALSE:
246 case INPUT:
247
248
249 rawText = new RawText(inTree);
250 break;
251 case TRUE:
252 try (AutoLFInputStream in = new AutoLFInputStream(
253 new FileInputStream(inTree), true)) {
254
255
256 rawText = new RawText(toByteArray(in, (int) inTree.length()));
257 }
258 break;
259 default:
260 throw new IllegalArgumentException(
261 "Unknown autocrlf option " + autoCRLF);
262 }
263 return rawText;
264 }
265
266 private static byte[] toByteArray(InputStream source, int upperSizeLimit)
267 throws IOException {
268 byte[] buffer = new byte[upperSizeLimit];
269 try {
270 int read = IO.readFully(source, buffer, 0);
271 if (read == upperSizeLimit)
272 return buffer;
273 else {
274 byte[] copy = new byte[read];
275 System.arraycopy(buffer, 0, copy, 0, read);
276 return copy;
277 }
278 } finally {
279 source.close();
280 }
281 }
282 }