-
Notifications
You must be signed in to change notification settings - Fork 7
/
JGitProvider.java
377 lines (324 loc) · 12.7 KB
/
JGitProvider.java
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*
* This file is part of git-commit-id-plugin-core by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
*
* git-commit-id-plugin-core 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.
*
* git-commit-id-plugin-core 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with git-commit-id-plugin-core. If not, see <http://www.gnu.org/licenses/>.
*/
package pl.project13.core;
import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.RevWalkUtils;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.util.FS;
import pl.project13.core.jgit.DescribeResult;
import pl.project13.core.jgit.JGitCommon;
import pl.project13.core.jgit.DescribeCommand;
import pl.project13.core.log.LogInterface;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import org.eclipse.jgit.storage.file.WindowCacheConfig;
import javax.annotation.Nonnull;
public class JGitProvider extends GitDataProvider {
private File dotGitDirectory;
private Repository git;
private ObjectReader objectReader;
private RevWalk revWalk;
private RevCommit evalCommit;
private JGitCommon jGitCommon;
@Nonnull
public static JGitProvider on(@Nonnull File dotGitDirectory, @Nonnull LogInterface log) {
return new JGitProvider(dotGitDirectory, log);
}
JGitProvider(@Nonnull File dotGitDirectory, @Nonnull LogInterface log) {
super(log);
this.dotGitDirectory = dotGitDirectory;
FS.FileStoreAttributes.setBackground(true);
this.jGitCommon = new JGitCommon(log);
}
@Override
public void init() throws GitCommitIdExecutionException {
git = getGitRepository();
objectReader = git.newObjectReader();
}
@Override
public String getBuildAuthorName() throws GitCommitIdExecutionException {
String userName = git.getConfig().getString("user", null, "name");
return Optional.ofNullable(userName).orElse("");
}
@Override
public String getBuildAuthorEmail() throws GitCommitIdExecutionException {
String userEmail = git.getConfig().getString("user", null, "email");
return Optional.ofNullable(userEmail).orElse("");
}
@Override
public void prepareGitToExtractMoreDetailedRepoInformation() throws GitCommitIdExecutionException {
try {
// more details parsed out bellow
Ref evaluateOnCommitReference = git.findRef(evaluateOnCommit);
ObjectId evaluateOnCommitResolvedObjectId = git.resolve(evaluateOnCommit);
if ((evaluateOnCommitReference == null) && (evaluateOnCommitResolvedObjectId == null)) {
throw new GitCommitIdExecutionException("Could not get " + evaluateOnCommit + " Ref, are you sure you have set the dotGitDirectory property of this plugin to a valid path?");
}
revWalk = new RevWalk(git);
ObjectId headObjectId;
if (evaluateOnCommitReference != null) {
headObjectId = evaluateOnCommitReference.getObjectId();
} else {
headObjectId = evaluateOnCommitResolvedObjectId;
}
if (headObjectId == null) {
throw new GitCommitIdExecutionException("Could not get " + evaluateOnCommit + " Ref, are you sure you have some commits in the dotGitDirectory?");
}
evalCommit = revWalk.parseCommit(headObjectId);
revWalk.markStart(evalCommit);
} catch (Exception e) {
throw new GitCommitIdExecutionException("Error", e);
}
}
@Override
public String getBranchName() throws GitCommitIdExecutionException {
try {
if (evalCommitIsNotHead()) {
return getBranchForCommitish();
} else {
return getBranchForHead();
}
} catch (IOException e) {
throw new GitCommitIdExecutionException(e);
}
}
private String getBranchForHead() throws IOException {
return git.getBranch();
}
private String getBranchForCommitish() throws GitCommitIdExecutionException {
try {
String commitId = getCommitId();
boolean evaluateOnCommitPointsToTag = git.getRefDatabase().getRefsByPrefix(Constants.R_TAGS)
.stream()
.anyMatch(ref -> Repository.shortenRefName(ref.getName()).equalsIgnoreCase(evaluateOnCommit));
if (evaluateOnCommitPointsToTag) {
// 'git branch --points-at' only works for <sha-objects> and <branch> names
// if the provided evaluateOnCommit points to a tag 'git branch --points-at' returns the commit-id instead
return commitId;
}
List<String> branchesForCommit = git.getRefDatabase().getRefsByPrefix(Constants.R_HEADS)
.stream()
.filter(ref -> commitId.equals(ref.getObjectId().name()))
.map(ref -> Repository.shortenRefName(ref.getName()))
.distinct()
.sorted()
.collect(Collectors.toList());
String branch = branchesForCommit.stream()
.collect(Collectors.joining(","));
if (branch != null && !branch.isEmpty()) {
return branch;
} else {
return commitId;
}
} catch (IOException e) {
throw new GitCommitIdExecutionException(e);
}
}
private boolean evalCommitIsNotHead() {
return (evaluateOnCommit != null) && !evaluateOnCommit.equals("HEAD");
}
@Override
public String getGitDescribe() throws GitCommitIdExecutionException {
return getGitDescribe(git);
}
@Override
public String getCommitId() throws GitCommitIdExecutionException {
return evalCommit.getName();
}
@Override
public String getAbbrevCommitId() throws GitCommitIdExecutionException {
return getAbbrevCommitId(objectReader, evalCommit, abbrevLength);
}
@Override
public boolean isDirty() throws GitCommitIdExecutionException {
try {
return JGitCommon.isRepositoryInDirtyState(git);
} catch (GitAPIException e) {
throw new GitCommitIdExecutionException("Failed to get git status: " + e.getMessage(), e);
}
}
@Override
public String getCommitAuthorName() throws GitCommitIdExecutionException {
return evalCommit.getAuthorIdent().getName();
}
@Override
public String getCommitAuthorEmail() throws GitCommitIdExecutionException {
return evalCommit.getAuthorIdent().getEmailAddress();
}
@Override
public String getCommitMessageFull() throws GitCommitIdExecutionException {
return evalCommit.getFullMessage().trim();
}
@Override
public String getCommitMessageShort() throws GitCommitIdExecutionException {
return evalCommit.getShortMessage().trim();
}
@Override
public String getCommitTime() throws GitCommitIdExecutionException {
long timeSinceEpoch = evalCommit.getCommitTime();
Date commitDate = new Date(timeSinceEpoch * 1000); // git is "by sec" and java is "by ms"
SimpleDateFormat smf = getSimpleDateFormatWithTimeZone();
return smf.format(commitDate);
}
@Override
public String getCommitAuthorTime() throws GitCommitIdExecutionException {
Date commitAuthorDate = evalCommit.getAuthorIdent().getWhen();
SimpleDateFormat smf = getSimpleDateFormatWithTimeZone();
return smf.format(commitAuthorDate);
}
@Override
public String getCommitCommitterTime() throws GitCommitIdExecutionException {
Date commitCommitterDate = evalCommit.getCommitterIdent().getWhen();
SimpleDateFormat smf = getSimpleDateFormatWithTimeZone();
return smf.format(commitCommitterDate);
}
@Override
public String getRemoteOriginUrl() throws GitCommitIdExecutionException {
String url = git.getConfig().getString("remote", "origin", "url");
return stripCredentialsFromOriginUrl(url);
}
@Override
public String getTags() throws GitCommitIdExecutionException {
try {
Repository repo = getGitRepository();
ObjectId headId = evalCommit.toObjectId();
Collection<String> tags = jGitCommon.getTags(repo, headId);
return String.join(",", tags);
} catch (GitAPIException e) {
log.error(String.format("Unable to extract tags from commit: %s", evalCommit.getName()), e);
return "";
}
}
@Override
public String getClosestTagName() throws GitCommitIdExecutionException {
Repository repo = getGitRepository();
try {
return jGitCommon.getClosestTagName(evaluateOnCommit, repo, gitDescribe);
} catch (Throwable t) {
// could not find any tags to describe
}
return "";
}
@Override
public String getClosestTagCommitCount() throws GitCommitIdExecutionException {
Repository repo = getGitRepository();
try {
return jGitCommon.getClosestTagCommitCount(evaluateOnCommit, repo, gitDescribe);
} catch (Throwable t) {
// could not find any tags to describe
}
return "";
}
@Override
public String getTotalCommitCount() throws GitCommitIdExecutionException {
try {
return String.valueOf(RevWalkUtils.count(revWalk, evalCommit, null));
} catch (Throwable t) {
// could not find any tags to describe
}
return "";
}
@Override
public void finalCleanUp() {
if (revWalk != null) {
revWalk.dispose();
}
// http://www.programcreek.com/java-api-examples/index.php?api=org.eclipse.jgit.storage.file.WindowCacheConfig
// Example 3
if (git != null) {
git.close();
// git.close() is not enough with jGit on Windows
// remove the references from packFile by initializing cache used in the repository
// fixing lock issues on Windows when repository has pack files
WindowCacheConfig config = new WindowCacheConfig();
config.install();
}
}
// Visible for testing
String getGitDescribe(@Nonnull Repository repository) throws GitCommitIdExecutionException {
try {
DescribeResult describeResult = DescribeCommand
.on(evaluateOnCommit, repository, log)
.apply(super.gitDescribe)
.call();
return describeResult.toString();
} catch (GitAPIException ex) {
ex.printStackTrace();
throw new GitCommitIdExecutionException("Unable to obtain git.commit.id.describe information", ex);
}
}
private String getAbbrevCommitId(ObjectReader objectReader, RevCommit headCommit, int abbrevLength) throws GitCommitIdExecutionException {
try {
AbbreviatedObjectId abbreviatedObjectId = objectReader.abbreviate(headCommit, abbrevLength);
return abbreviatedObjectId.name();
} catch (IOException e) {
throw new GitCommitIdExecutionException("Unable to abbreviate commit id! " +
"You may want to investigate the <abbrevLength/> element in your configuration.", e);
}
}
@Nonnull
private Repository getGitRepository() throws GitCommitIdExecutionException {
Repository repository;
FileRepositoryBuilder repositoryBuilder = new FileRepositoryBuilder();
try {
repository = repositoryBuilder
.setGitDir(dotGitDirectory)
.readEnvironment() // scan environment GIT_* variables
.findGitDir() // scan up the file system tree
.build();
} catch (IOException e) {
throw new GitCommitIdExecutionException("Could not initialize repository...", e);
}
if (repository == null) {
throw new GitCommitIdExecutionException("Could not create git repository. Are you sure '" + dotGitDirectory + "' is the valid Git root for your project?");
}
return repository;
}
@Override
public AheadBehind getAheadBehind() throws GitCommitIdExecutionException {
try {
if (!offline) {
fetch();
}
Optional<BranchTrackingStatus> branchTrackingStatus = Optional.ofNullable(BranchTrackingStatus.of(git, getBranchName()));
return branchTrackingStatus.map(bts -> AheadBehind.of(bts.getAheadCount(), bts.getBehindCount()))
.orElse(AheadBehind.NO_REMOTE);
} catch (Exception e) {
throw new GitCommitIdExecutionException("Failed to read ahead behind count: " + e.getMessage(), e);
}
}
private void fetch() {
FetchCommand fetchCommand = Git.wrap(git).fetch();
try {
fetchCommand.setThin(true).call();
} catch (Exception e) {
log.error("Failed to perform fetch", e);
}
}
// SETTERS FOR TESTS ----------------------------------------------------
public void setRepository(Repository git) {
this.git = git;
}
}