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