11package io .jenkins .plugins .git .forensics .miner ;
22
33import java .io .IOException ;
4+ import java .nio .file .LinkOption ;
5+ import java .nio .file .Paths ;
6+ import java .util .Collection ;
47import java .util .List ;
58import java .util .Set ;
69import java .util .stream .Collectors ;
710
811import org .apache .commons .lang3 .StringUtils ;
912import org .eclipse .jgit .api .Git ;
1013import org .eclipse .jgit .api .errors .GitAPIException ;
14+ import org .eclipse .jgit .dircache .InvalidPathException ;
1115import org .eclipse .jgit .lib .Constants ;
1216import org .eclipse .jgit .lib .ObjectId ;
1317import org .eclipse .jgit .lib .PersonIdent ;
1418import org .eclipse .jgit .lib .Repository ;
1519import org .eclipse .jgit .revwalk .RevCommit ;
1620
1721import edu .umd .cs .findbugs .annotations .Nullable ;
22+ import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
1823
1924import org .jenkinsci .plugins .gitclient .GitClient ;
2025import org .jenkinsci .plugins .gitclient .RepositoryCallback ;
26+ import hudson .FilePath ;
2127import hudson .remoting .VirtualChannel ;
2228
2329import io .jenkins .plugins .forensics .miner .FileStatistics ;
3137 * @see io.jenkins.plugins.forensics.miner.FileStatistics
3238 * @see io.jenkins.plugins.git.forensics.miner.FilesCollector
3339 */
40+ @ SuppressFBWarnings (value = "SE" , justification = "GitClient implementation is Serializable" )
3441public class GitRepositoryMiner extends RepositoryMiner {
3542 private static final long serialVersionUID = 1157958118716013983L ;
3643
3744 private final GitClient gitClient ;
45+ private final FilePath workspace ;
3846
3947 GitRepositoryMiner (final GitClient gitClient ) {
48+ super ();
49+
4050 this .gitClient = gitClient ;
51+ workspace = gitClient .getWorkTree ();
4152 }
4253
4354 @ Override
44- public RepositoryStatistics mine () throws InterruptedException {
55+ public RepositoryStatistics mine (final Collection < String > relativeFileNames ) throws InterruptedException {
4556 try {
46- return gitClient .withRepository (new RepositoryStatisticsCallback ());
57+ String workspacePath = getWorkspacePath ();
58+
59+ long nano = System .nanoTime ();
60+ RepositoryStatistics statistics = gitClient .withRepository (
61+ new RepositoryStatisticsCallback (workspacePath , relativeFileNames ));
62+ statistics .logInfo ("Mining of the Git repository took %d seconds" ,
63+ 1 + (System .nanoTime () - nano ) / 1_000_000_000L );
64+ return statistics ;
4765 }
4866 catch (IOException exception ) {
4967 RepositoryStatistics statistics = new RepositoryStatistics ();
@@ -52,24 +70,46 @@ public RepositoryStatistics mine() throws InterruptedException {
5270 }
5371 }
5472
73+ private String getWorkspacePath () {
74+ try {
75+ return Paths .get (workspace .getRemote ()).toAbsolutePath ().normalize ().toRealPath (LinkOption .NOFOLLOW_LINKS ).toString ();
76+ }
77+ catch (IOException | InvalidPathException exception ) {
78+ return workspace .getRemote ();
79+ }
80+ }
81+
5582 private static class RepositoryStatisticsCallback implements RepositoryCallback <RepositoryStatistics > {
5683 private static final long serialVersionUID = 7667073858514128136L ;
84+ private final String workspacePath ;
85+ private final Collection <String > paths ;
86+
87+ RepositoryStatisticsCallback (final String workspacePath , final Collection <String > paths ) {
88+ this .workspacePath = workspacePath ;
89+ this .paths = paths ;
90+ }
5791
5892 @ Override
5993 public RepositoryStatistics invoke (final Repository repository , final VirtualChannel channel ) {
6094 try {
61- ObjectId head = repository .resolve (Constants .HEAD );
62- Set <String > files = new FilesCollector (repository ).findAllFor (head );
63- return analyze (repository , files );
95+ if (paths .isEmpty ()) { // scan whole repository
96+ ObjectId head = repository .resolve (Constants .HEAD );
97+ Set <String > files = new FilesCollector (repository ).findAllFor (head );
98+ return analyze (repository , files );
99+ }
100+ return analyze (repository , paths );
64101 }
65102 catch (IOException exception ) {
66103 RepositoryStatistics statistics = new RepositoryStatistics ();
67104 statistics .logException (exception , "Can't obtain HEAD of repository." );
68105 return statistics ;
69106 }
107+ finally {
108+ repository .close ();
109+ }
70110 }
71111
72- RepositoryStatistics analyze (final Repository repository , final Set <String > files ) {
112+ RepositoryStatistics analyze (final Repository repository , final Collection <String > files ) {
73113 RepositoryStatistics statistics = new RepositoryStatistics ();
74114 statistics .logInfo ("Invoking Git miner to create creates statistics for all available files" );
75115
@@ -78,12 +118,14 @@ RepositoryStatistics analyze(final Repository repository, final Set<String> file
78118 .collect (Collectors .toList ());
79119 statistics .addAll (fileStatistics );
80120
121+ statistics .logInfo ("-> created statistics for %d files" , statistics .size ());
122+
81123 return statistics ;
82124 }
83125
84126 private FileStatistics analyzeHistory (final Repository repository , final String fileName ,
85127 final RepositoryStatistics statistics ) {
86- FileStatistics fileStatistics = new FileStatistics (fileName );
128+ FileStatistics fileStatistics = new FileStatistics (workspacePath + "/" + fileName );
87129
88130 try {
89131 Git git = new Git (repository );
0 commit comments