1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.scm.provider.git.jgit.command;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.UnsupportedEncodingException;
24 import java.net.URI;
25 import java.net.URLEncoder;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Date;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Set;
32 import java.util.function.BiConsumer;
33
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.maven.scm.ScmFile;
36 import org.apache.maven.scm.ScmFileSet;
37 import org.apache.maven.scm.ScmFileStatus;
38 import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
39 import org.eclipse.jgit.api.AddCommand;
40 import org.eclipse.jgit.api.Git;
41 import org.eclipse.jgit.api.PushCommand;
42 import org.eclipse.jgit.api.RmCommand;
43 import org.eclipse.jgit.api.Status;
44 import org.eclipse.jgit.api.errors.GitAPIException;
45 import org.eclipse.jgit.api.errors.InvalidRemoteException;
46 import org.eclipse.jgit.api.errors.TransportException;
47 import org.eclipse.jgit.diff.DiffEntry;
48 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
49 import org.eclipse.jgit.diff.DiffFormatter;
50 import org.eclipse.jgit.diff.RawTextComparator;
51 import org.eclipse.jgit.errors.CorruptObjectException;
52 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
53 import org.eclipse.jgit.errors.MissingObjectException;
54 import org.eclipse.jgit.errors.StopWalkException;
55 import org.eclipse.jgit.lib.Constants;
56 import org.eclipse.jgit.lib.ObjectId;
57 import org.eclipse.jgit.lib.ProgressMonitor;
58 import org.eclipse.jgit.lib.Ref;
59 import org.eclipse.jgit.lib.Repository;
60 import org.eclipse.jgit.lib.RepositoryBuilder;
61 import org.eclipse.jgit.lib.StoredConfig;
62 import org.eclipse.jgit.lib.TextProgressMonitor;
63 import org.eclipse.jgit.revwalk.RevCommit;
64 import org.eclipse.jgit.revwalk.RevFlag;
65 import org.eclipse.jgit.revwalk.RevSort;
66 import org.eclipse.jgit.revwalk.RevWalk;
67 import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
68 import org.eclipse.jgit.revwalk.filter.RevFilter;
69 import org.eclipse.jgit.transport.CredentialsProvider;
70 import org.eclipse.jgit.transport.PushResult;
71 import org.eclipse.jgit.transport.RefSpec;
72 import org.eclipse.jgit.transport.RemoteRefUpdate;
73 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
74 import org.eclipse.jgit.util.io.DisabledOutputStream;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 import static org.eclipse.jgit.lib.Constants.R_TAGS;
79
80
81
82
83
84
85
86
87 public class JGitUtils {
88 private static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
89
90 private JGitUtils() {
91
92 }
93
94
95
96
97
98
99 public static Git openRepo(File basedir) throws IOException {
100 return new Git(new RepositoryBuilder()
101 .readEnvironment()
102 .findGitDir(basedir)
103 .setMustExist(true)
104 .build());
105 }
106
107
108
109
110
111 public static void closeRepo(Git git) {
112 if (git != null && git.getRepository() != null) {
113 git.getRepository().close();
114 }
115 }
116
117
118
119
120
121
122 public static ProgressMonitor getMonitor() {
123
124 return new TextProgressMonitor();
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138 public static CredentialsProvider prepareSession(Git git, GitScmProviderRepository repository) {
139 StoredConfig config = git.getRepository().getConfig();
140 config.setString("remote", "origin", "url", repository.getFetchUrl());
141 config.setString("remote", "origin", "pushURL", repository.getPushUrl());
142
143
144 String password = StringUtils.isNotBlank(repository.getPassword())
145 ? repository.getPassword().trim()
146 : "no-pwd-defined";
147
148
149 try {
150 password = URLEncoder.encode(password, "UTF-8");
151 } catch (UnsupportedEncodingException e) {
152
153
154 System.out.println("Ignore UnsupportedEncodingException when trying to encode password");
155 }
156 LOGGER.info("fetch url: " + repository.getFetchUrl().replace(password, "******"));
157 LOGGER.info("push url: " + repository.getPushUrl().replace(password, "******"));
158 return getCredentials(repository);
159 }
160
161
162
163
164
165
166
167
168
169
170
171 public static CredentialsProvider getCredentials(GitScmProviderRepository repository) {
172 if (StringUtils.isNotBlank(repository.getUser()) && StringUtils.isNotBlank(repository.getPassword())) {
173 return new UsernamePasswordCredentialsProvider(
174 repository.getUser().trim(), repository.getPassword().trim());
175 }
176
177 return null;
178 }
179
180 public static Iterable<PushResult> push(Git git, GitScmProviderRepository repo, RefSpec refSpec)
181 throws GitAPIException, InvalidRemoteException, TransportException {
182 CredentialsProvider credentials = prepareSession(git, repo);
183 PushCommand command = git.push()
184 .setRefSpecs(refSpec)
185 .setCredentialsProvider(credentials)
186 .setTransportConfigCallback(
187 new JGitTransportConfigCallback(new ScmProviderAwareSshdSessionFactory(repo, LOGGER)));
188
189 Iterable<PushResult> pushResultList = command.call();
190 for (PushResult pushResult : pushResultList) {
191 Collection<RemoteRefUpdate> ru = pushResult.getRemoteUpdates();
192 for (RemoteRefUpdate remoteRefUpdate : ru) {
193 LOGGER.info(remoteRefUpdate.getStatus() + " - " + remoteRefUpdate);
194 }
195 }
196 return pushResultList;
197 }
198
199
200
201
202
203
204
205 public static boolean hasCommits(Repository repo) {
206 if (repo != null && repo.getDirectory().exists()) {
207 return (new File(repo.getDirectory(), "objects").list().length > 2)
208 || (new File(repo.getDirectory(), "objects/pack").list().length > 0);
209 }
210 return false;
211 }
212
213
214
215
216
217
218
219
220
221
222
223
224 public static List<ScmFile> getFilesInCommit(Repository repository, RevCommit commit)
225 throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException {
226 return getFilesInCommit(repository, commit, null);
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 public static List<ScmFile> getFilesInCommit(Repository repository, RevCommit commit, File baseDir)
244 throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException {
245 List<ScmFile> list = new ArrayList<>();
246 if (JGitUtils.hasCommits(repository)) {
247
248 try (RevWalk rw = new RevWalk(repository);
249 DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
250 RevCommit realParent = commit.getParentCount() > 0 ? commit.getParent(0) : commit;
251 RevCommit parent = rw.parseCommit(realParent.getId());
252 df.setRepository(repository);
253 df.setDiffComparator(RawTextComparator.DEFAULT);
254 df.setDetectRenames(true);
255 List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree());
256 for (DiffEntry diff : diffs) {
257 final String path;
258 if (baseDir != null) {
259 path = relativize(baseDir.toURI(), new File(repository.getWorkTree(), diff.getNewPath()));
260 } else {
261 path = diff.getNewPath();
262 }
263 list.add(new ScmFile(path, ScmFileStatus.CHECKED_IN));
264 }
265 }
266 }
267 return list;
268 }
269
270
271
272
273
274
275
276 public static ScmFileStatus getScmFileStatus(ChangeType changeType) {
277 switch (changeType) {
278 case ADD:
279 return ScmFileStatus.ADDED;
280 case MODIFY:
281 return ScmFileStatus.MODIFIED;
282 case DELETE:
283 return ScmFileStatus.DELETED;
284 case RENAME:
285 return ScmFileStatus.RENAMED;
286 case COPY:
287 return ScmFileStatus.COPIED;
288 default:
289 return ScmFileStatus.UNKNOWN;
290 }
291 }
292
293
294
295
296
297
298
299
300
301
302 public static List<ScmFile> addAllFiles(Git git, ScmFileSet fileSet) throws GitAPIException {
303 URI workingCopyRootUri = git.getRepository().getWorkTree().toURI();
304 AddCommand add = git.add();
305 callWithRepositoryRelativeFilePath(
306 (relativeFile, absoluteFile) -> {
307 if (absoluteFile.exists()) {
308 add.addFilepattern(relativeFile);
309 }
310 },
311 workingCopyRootUri,
312 fileSet);
313 add.call();
314
315 Status status = git.status().call();
316
317 Set<String> allInIndex = new HashSet<>();
318 allInIndex.addAll(status.getAdded());
319 allInIndex.addAll(status.getChanged());
320 return getScmFilesForAllFileSetFilesContainedInRepoPath(
321 workingCopyRootUri, fileSet, allInIndex, ScmFileStatus.ADDED);
322 }
323
324
325
326
327
328
329
330
331
332
333 public static List<ScmFile> removeAllFiles(Git git, ScmFileSet fileSet) throws GitAPIException {
334 URI workingCopyRootUri = git.getRepository().getWorkTree().toURI();
335 RmCommand remove = git.rm();
336 callWithRepositoryRelativeFilePath(
337 (relativeFile, absoluteFile) -> remove.addFilepattern(relativeFile), workingCopyRootUri, fileSet);
338 remove.call();
339
340 Status status = git.status().call();
341
342 Set<String> allInIndex = new HashSet<>(status.getRemoved());
343 return getScmFilesForAllFileSetFilesContainedInRepoPath(
344 workingCopyRootUri, fileSet, allInIndex, ScmFileStatus.DELETED);
345 }
346
347
348
349
350
351
352
353
354 private static void callWithRepositoryRelativeFilePath(
355 BiConsumer<String, File> fileCallback, URI workingCopyRootUri, ScmFileSet fileSet) {
356 for (File file : fileSet.getFileList()) {
357 if (!file.isAbsolute()) {
358 file = new File(fileSet.getBasedir().getPath(), file.getPath());
359 }
360 String path = relativize(workingCopyRootUri, file);
361 fileCallback.accept(path, file);
362 }
363 }
364
365 private static List<ScmFile> getScmFilesForAllFileSetFilesContainedInRepoPath(
366 URI workingCopyRootUri, ScmFileSet fileSet, Set<String> repoFilePaths, ScmFileStatus fileStatus) {
367 List<ScmFile> files = new ArrayList<>(repoFilePaths.size());
368 callWithRepositoryRelativeFilePath(
369 (relativeFile, absoluteFile) -> {
370
371 if (repoFilePaths.contains(relativeFile)) {
372
373 ScmFile scmfile =
374 new ScmFile(relativize(fileSet.getBasedir().toURI(), absoluteFile), fileStatus);
375 files.add(scmfile);
376 }
377 },
378 workingCopyRootUri,
379 fileSet);
380 return files;
381 }
382
383 private static String relativize(URI baseUri, File f) {
384 String path = f.getPath();
385 if (f.isAbsolute()) {
386 path = baseUri.relativize(new File(path).toURI()).getPath();
387 }
388 return path;
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 public static List<RevCommit> getRevCommits(
407 Repository repo,
408 RevSort[] sortings,
409 String fromRev,
410 String toRev,
411 final Date fromDate,
412 final Date toDate,
413 int maxLines)
414 throws IOException, MissingObjectException, IncorrectObjectTypeException {
415
416 List<RevCommit> revs = new ArrayList<>();
417
418 ObjectId fromRevId = fromRev != null ? repo.resolve(fromRev) : null;
419 ObjectId toRevId = toRev != null ? repo.resolve(toRev) : null;
420
421 if (sortings == null || sortings.length == 0) {
422 sortings = new RevSort[] {RevSort.TOPO, RevSort.COMMIT_TIME_DESC};
423 }
424
425 try (RevWalk walk = new RevWalk(repo)) {
426 for (final RevSort s : sortings) {
427 walk.sort(s, true);
428 }
429
430 if (fromDate != null && toDate != null) {
431
432 walk.setRevFilter(new RevFilter() {
433 @Override
434 public boolean include(RevWalk walker, RevCommit cmit)
435 throws StopWalkException, MissingObjectException, IncorrectObjectTypeException,
436 IOException {
437 int cmtTime = cmit.getCommitTime();
438
439 return (cmtTime >= (fromDate.getTime() / 1000)) && (cmtTime <= (toDate.getTime() / 1000));
440 }
441
442 @Override
443 public RevFilter clone() {
444 return this;
445 }
446 });
447 } else {
448 if (fromDate != null) {
449 walk.setRevFilter(CommitTimeRevFilter.after(fromDate));
450 }
451 if (toDate != null) {
452 walk.setRevFilter(CommitTimeRevFilter.before(toDate));
453 }
454 }
455
456 if (fromRevId != null) {
457 RevCommit c = walk.parseCommit(fromRevId);
458 c.add(RevFlag.UNINTERESTING);
459 RevCommit real = walk.parseCommit(c);
460 walk.markUninteresting(real);
461 }
462
463 if (toRevId != null) {
464 RevCommit c = walk.parseCommit(toRevId);
465 c.remove(RevFlag.UNINTERESTING);
466 RevCommit real = walk.parseCommit(c);
467 walk.markStart(real);
468 } else {
469 final ObjectId head = repo.resolve(Constants.HEAD);
470 if (head == null) {
471 throw new RuntimeException("Cannot resolve " + Constants.HEAD);
472 }
473 RevCommit real = walk.parseCommit(head);
474 walk.markStart(real);
475 }
476
477 int n = 0;
478 for (final RevCommit c : walk) {
479 n++;
480 if (maxLines != -1 && n > maxLines) {
481 break;
482 }
483
484 revs.add(c);
485 }
486 return revs;
487 }
488 }
489
490
491
492
493
494
495
496
497 public static List<String> getTags(Repository repo, RevCommit commit) throws IOException {
498 List<Ref> refList = repo.getRefDatabase().getRefsByPrefix(R_TAGS);
499
500 try (RevWalk revWalk = new RevWalk(repo)) {
501 ObjectId commitId = commit.getId();
502 List<String> result = new ArrayList<>();
503
504 for (Ref ref : refList) {
505 ObjectId tagId = ref.getObjectId();
506 RevCommit tagCommit = revWalk.parseCommit(tagId);
507 if (commitId.equals(tagCommit.getId())) {
508 result.add(ref.getName().substring(R_TAGS.length()));
509 }
510 }
511 return result;
512 }
513 }
514 }