PageRenderTime 35ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/main/java/pl/project13/core/JGitProvider.java

http://github.com/ktoso/maven-git-commit-id-plugin
Java | 363 lines | 283 code | 52 blank | 28 comment | 25 complexity | 797b7fc3f09341660bcb24ccf8e9610b MD5 | raw file
Possible License(s): LGPL-3.0
  1. /*
  2. * This file is part of git-commit-id-plugin by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
  3. *
  4. * git-commit-id-plugin is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * git-commit-id-plugin is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License
  15. * along with git-commit-id-plugin. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package pl.project13.core;
  18. import com.google.common.annotations.VisibleForTesting;
  19. import org.eclipse.jgit.api.FetchCommand;
  20. import org.eclipse.jgit.api.Git;
  21. import org.eclipse.jgit.api.errors.GitAPIException;
  22. import org.eclipse.jgit.lib.*;
  23. import org.eclipse.jgit.revwalk.RevCommit;
  24. import org.eclipse.jgit.revwalk.RevWalk;
  25. import org.eclipse.jgit.revwalk.RevWalkUtils;
  26. import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
  27. import pl.project13.core.jgit.DescribeResult;
  28. import pl.project13.core.jgit.JGitCommon;
  29. import pl.project13.core.jgit.DescribeCommand;
  30. import pl.project13.core.log.LoggerBridge;
  31. import java.io.File;
  32. import java.io.IOException;
  33. import java.text.SimpleDateFormat;
  34. import java.util.*;
  35. import java.util.stream.Collectors;
  36. import org.eclipse.jgit.storage.file.WindowCacheConfig;
  37. import javax.annotation.Nonnull;
  38. public class JGitProvider extends GitDataProvider {
  39. private File dotGitDirectory;
  40. private Repository git;
  41. private ObjectReader objectReader;
  42. private RevWalk revWalk;
  43. private RevCommit evalCommit;
  44. private JGitCommon jGitCommon;
  45. @Nonnull
  46. public static JGitProvider on(@Nonnull File dotGitDirectory, @Nonnull LoggerBridge log) {
  47. return new JGitProvider(dotGitDirectory, log);
  48. }
  49. JGitProvider(@Nonnull File dotGitDirectory, @Nonnull LoggerBridge log) {
  50. super(log);
  51. this.dotGitDirectory = dotGitDirectory;
  52. this.jGitCommon = new JGitCommon(log);
  53. }
  54. @Override
  55. public void init() throws GitCommitIdExecutionException {
  56. git = getGitRepository();
  57. objectReader = git.newObjectReader();
  58. }
  59. @Override
  60. public String getBuildAuthorName() throws GitCommitIdExecutionException {
  61. String userName = git.getConfig().getString("user", null, "name");
  62. return Optional.ofNullable(userName).orElse("");
  63. }
  64. @Override
  65. public String getBuildAuthorEmail() throws GitCommitIdExecutionException {
  66. String userEmail = git.getConfig().getString("user", null, "email");
  67. return Optional.ofNullable(userEmail).orElse("");
  68. }
  69. @Override
  70. public void prepareGitToExtractMoreDetailedRepoInformation() throws GitCommitIdExecutionException {
  71. try {
  72. // more details parsed out bellow
  73. Ref evaluateOnCommitReference = git.findRef(evaluateOnCommit);
  74. ObjectId evaluateOnCommitResolvedObjectId = git.resolve(evaluateOnCommit);
  75. if ((evaluateOnCommitReference == null) && (evaluateOnCommitResolvedObjectId == null)) {
  76. throw new GitCommitIdExecutionException("Could not get " + evaluateOnCommit + " Ref, are you sure you have set the dotGitDirectory property of this plugin to a valid path?");
  77. }
  78. revWalk = new RevWalk(git);
  79. ObjectId headObjectId;
  80. if (evaluateOnCommitReference != null) {
  81. headObjectId = evaluateOnCommitReference.getObjectId();
  82. } else {
  83. headObjectId = evaluateOnCommitResolvedObjectId;
  84. }
  85. if (headObjectId == null) {
  86. throw new GitCommitIdExecutionException("Could not get " + evaluateOnCommit + " Ref, are you sure you have some commits in the dotGitDirectory?");
  87. }
  88. evalCommit = revWalk.parseCommit(headObjectId);
  89. revWalk.markStart(evalCommit);
  90. } catch (Exception e) {
  91. throw new GitCommitIdExecutionException("Error", e);
  92. }
  93. }
  94. @Override
  95. public String getBranchName() throws GitCommitIdExecutionException {
  96. try {
  97. if (evalCommitIsNotHead()) {
  98. return getBranchForCommitish();
  99. } else {
  100. return getBranchForHead();
  101. }
  102. } catch (IOException e) {
  103. throw new GitCommitIdExecutionException(e);
  104. }
  105. }
  106. private String getBranchForHead() throws IOException {
  107. return git.getBranch();
  108. }
  109. private String getBranchForCommitish() throws GitCommitIdExecutionException {
  110. try {
  111. String commitId = getCommitId();
  112. boolean evaluateOnCommitPointsToTag = git.getRefDatabase().getRefsByPrefix(Constants.R_TAGS)
  113. .stream()
  114. .anyMatch(ref -> Repository.shortenRefName(ref.getName()).equalsIgnoreCase(evaluateOnCommit));
  115. if (evaluateOnCommitPointsToTag) {
  116. // 'git branch --points-at' only works for <sha-objects> and <branch> names
  117. // if the provided evaluateOnCommit points to a tag 'git branch --points-at' returns the commit-id instead
  118. return commitId;
  119. }
  120. List<String> branchesForCommit = git.getRefDatabase().getRefsByPrefix(Constants.R_HEADS)
  121. .stream()
  122. .filter(ref -> commitId.equals(ref.getObjectId().name()))
  123. .map(ref -> Repository.shortenRefName(ref.getName()))
  124. .distinct()
  125. .sorted()
  126. .collect(Collectors.toList());
  127. String branch = branchesForCommit.stream()
  128. .collect(Collectors.joining(","));
  129. if (branch != null && !branch.isEmpty()) {
  130. return branch;
  131. } else {
  132. return commitId;
  133. }
  134. } catch (IOException e) {
  135. throw new GitCommitIdExecutionException(e);
  136. }
  137. }
  138. private boolean evalCommitIsNotHead() {
  139. return (evaluateOnCommit != null) && !evaluateOnCommit.equals("HEAD");
  140. }
  141. @Override
  142. public String getGitDescribe() throws GitCommitIdExecutionException {
  143. return getGitDescribe(git);
  144. }
  145. @Override
  146. public String getCommitId() throws GitCommitIdExecutionException {
  147. return evalCommit.getName();
  148. }
  149. @Override
  150. public String getAbbrevCommitId() throws GitCommitIdExecutionException {
  151. return getAbbrevCommitId(objectReader, evalCommit, abbrevLength);
  152. }
  153. @Override
  154. public boolean isDirty() throws GitCommitIdExecutionException {
  155. try {
  156. return JGitCommon.isRepositoryInDirtyState(git);
  157. } catch (GitAPIException e) {
  158. throw new GitCommitIdExecutionException("Failed to get git status: " + e.getMessage(), e);
  159. }
  160. }
  161. @Override
  162. public String getCommitAuthorName() throws GitCommitIdExecutionException {
  163. return evalCommit.getAuthorIdent().getName();
  164. }
  165. @Override
  166. public String getCommitAuthorEmail() throws GitCommitIdExecutionException {
  167. return evalCommit.getAuthorIdent().getEmailAddress();
  168. }
  169. @Override
  170. public String getCommitMessageFull() throws GitCommitIdExecutionException {
  171. return evalCommit.getFullMessage().trim();
  172. }
  173. @Override
  174. public String getCommitMessageShort() throws GitCommitIdExecutionException {
  175. return evalCommit.getShortMessage().trim();
  176. }
  177. @Override
  178. public String getCommitTime() throws GitCommitIdExecutionException {
  179. long timeSinceEpoch = evalCommit.getCommitTime();
  180. Date commitDate = new Date(timeSinceEpoch * 1000); // git is "by sec" and java is "by ms"
  181. SimpleDateFormat smf = getSimpleDateFormatWithTimeZone();
  182. return smf.format(commitDate);
  183. }
  184. @Override
  185. public String getRemoteOriginUrl() throws GitCommitIdExecutionException {
  186. String url = git.getConfig().getString("remote", "origin", "url");
  187. return stripCredentialsFromOriginUrl(url);
  188. }
  189. @Override
  190. public String getTags() throws GitCommitIdExecutionException {
  191. try {
  192. Repository repo = getGitRepository();
  193. ObjectId headId = evalCommit.toObjectId();
  194. Collection<String> tags = jGitCommon.getTags(repo, headId);
  195. return String.join(",", tags);
  196. } catch (GitAPIException e) {
  197. log.error("Unable to extract tags from commit: {} ({})", evalCommit.getName(), e.getClass().getName());
  198. return "";
  199. }
  200. }
  201. @Override
  202. public String getClosestTagName() throws GitCommitIdExecutionException {
  203. Repository repo = getGitRepository();
  204. try {
  205. return jGitCommon.getClosestTagName(evaluateOnCommit, repo, gitDescribe);
  206. } catch (Throwable t) {
  207. // could not find any tags to describe
  208. }
  209. return "";
  210. }
  211. @Override
  212. public String getClosestTagCommitCount() throws GitCommitIdExecutionException {
  213. Repository repo = getGitRepository();
  214. try {
  215. return jGitCommon.getClosestTagCommitCount(evaluateOnCommit, repo, gitDescribe);
  216. } catch (Throwable t) {
  217. // could not find any tags to describe
  218. }
  219. return "";
  220. }
  221. @Override
  222. public String getTotalCommitCount() throws GitCommitIdExecutionException {
  223. try {
  224. return String.valueOf(RevWalkUtils.count(revWalk, evalCommit, null));
  225. } catch (Throwable t) {
  226. // could not find any tags to describe
  227. }
  228. return "";
  229. }
  230. @Override
  231. public void finalCleanUp() {
  232. if (revWalk != null) {
  233. revWalk.dispose();
  234. }
  235. // http://www.programcreek.com/java-api-examples/index.php?api=org.eclipse.jgit.storage.file.WindowCacheConfig
  236. // Example 3
  237. if (git != null) {
  238. git.close();
  239. // git.close() is not enough with jGit on Windows
  240. // remove the references from packFile by initializing cache used in the repository
  241. // fixing lock issues on Windows when repository has pack files
  242. WindowCacheConfig config = new WindowCacheConfig();
  243. config.install();
  244. }
  245. }
  246. @VisibleForTesting String getGitDescribe(@Nonnull Repository repository) throws GitCommitIdExecutionException {
  247. try {
  248. DescribeResult describeResult = DescribeCommand
  249. .on(evaluateOnCommit, repository, log)
  250. .apply(super.gitDescribe)
  251. .call();
  252. return describeResult.toString();
  253. } catch (GitAPIException ex) {
  254. ex.printStackTrace();
  255. throw new GitCommitIdExecutionException("Unable to obtain git.commit.id.describe information", ex);
  256. }
  257. }
  258. private String getAbbrevCommitId(ObjectReader objectReader, RevCommit headCommit, int abbrevLength) throws GitCommitIdExecutionException {
  259. try {
  260. AbbreviatedObjectId abbreviatedObjectId = objectReader.abbreviate(headCommit, abbrevLength);
  261. return abbreviatedObjectId.name();
  262. } catch (IOException e) {
  263. throw new GitCommitIdExecutionException("Unable to abbreviate commit id! " +
  264. "You may want to investigate the <abbrevLength/> element in your configuration.", e);
  265. }
  266. }
  267. @Nonnull
  268. private Repository getGitRepository() throws GitCommitIdExecutionException {
  269. Repository repository;
  270. FileRepositoryBuilder repositoryBuilder = new FileRepositoryBuilder();
  271. try {
  272. repository = repositoryBuilder
  273. .setGitDir(dotGitDirectory)
  274. .readEnvironment() // scan environment GIT_* variables
  275. .findGitDir() // scan up the file system tree
  276. .build();
  277. } catch (IOException e) {
  278. throw new GitCommitIdExecutionException("Could not initialize repository...", e);
  279. }
  280. if (repository == null) {
  281. throw new GitCommitIdExecutionException("Could not create git repository. Are you sure '" + dotGitDirectory + "' is the valid Git root for your project?");
  282. }
  283. return repository;
  284. }
  285. @Override
  286. public AheadBehind getAheadBehind() throws GitCommitIdExecutionException {
  287. try {
  288. if (!offline) {
  289. fetch();
  290. }
  291. Optional<BranchTrackingStatus> branchTrackingStatus = Optional.ofNullable(BranchTrackingStatus.of(git, getBranchName()));
  292. return branchTrackingStatus.map(bts -> AheadBehind.of(bts.getAheadCount(), bts.getBehindCount()))
  293. .orElse(AheadBehind.NO_REMOTE);
  294. } catch (Exception e) {
  295. throw new GitCommitIdExecutionException("Failed to read ahead behind count: " + e.getMessage(), e);
  296. }
  297. }
  298. private void fetch() {
  299. FetchCommand fetchCommand = Git.wrap(git).fetch();
  300. try {
  301. fetchCommand.setThin(true).call();
  302. } catch (Exception e) {
  303. log.error("Failed to perform fetch", e);
  304. }
  305. }
  306. // SETTERS FOR TESTS ----------------------------------------------------
  307. @VisibleForTesting
  308. public void setRepository(Repository git) {
  309. this.git = git;
  310. }
  311. }