All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.project13.core.JGitProvider Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of git-commit-id-plugin-core by Konrad 'ktoso' Malawski 
 *
 * 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 .
 */

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 (currently set to " + dotGitDirectory + ")?");
      }
      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 (currently set to " + dotGitDirectory + ")?");
      }
      evalCommit = revWalk.parseCommit(headObjectId);
      revWalk.markStart(evalCommit);
    } catch (GitCommitIdExecutionException e) {
      throw e;
    } catch (Exception e) {
      throw new GitCommitIdExecutionException(e.getMessage(), 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  and  names
        // if the provided evaluateOnCommit points to a tag 'git branch --points-at' returns the commit-id instead
        return commitId;
      }

      List 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 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 getTag() throws GitCommitIdExecutionException {
    try {
      Repository repo = getGitRepository();
      ObjectId headId = evalCommit.toObjectId();
      Collection tags = jGitCommon.getTag(repo, headId);
      return String.join(",", tags);
    } catch (GitAPIException e) {
      log.error(String.format("Unable to extract tag 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  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 = 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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy