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

pl.project13.jgit.JGitCommon Maven / Gradle / Ivy

Go to download

This plugin makes basic repository information available through maven resources. This can be used to display "what version is this?" or "who has deployed this and when, from which branch?" information at runtime, making it easy to find things like "oh, that isn't deployed yet, I'll test it tomorrow" and making both testers and developers life easier. See https://github.com/git-commit-id/maven-git-commit-id-plugin

There is a newer version: 4.9.10
Show newest version
/*
 * This file is part of git-commit-id-plugin by Konrad 'ktoso' Malawski 
 *
 * git-commit-id-plugin 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 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.  If not, see .
 */

package pl.project13.jgit;

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.jetbrains.annotations.NotNull;

import pl.project13.jgit.dummy.DatedRevTag;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import pl.project13.maven.git.log.LoggerBridge;

public class JGitCommon {

  private final LoggerBridge log;

  public JGitCommon(LoggerBridge log) {
    this.log = log;
  }

  public Collection getTags(Repository repo, final ObjectId headId) throws GitAPIException{
    RevWalk walk = null;
    try {
      Git git = Git.wrap(repo);
      walk = new RevWalk(repo);
      List tagRefs = git.tagList().call();

      final RevWalk finalWalk = walk;
      Collection tagsForHeadCommit = Collections2.filter(tagRefs, new Predicate() {
        @Override public boolean apply(Ref tagRef) {
        boolean lightweightTag = tagRef.getObjectId().equals(headId);

          try {
            // TODO make this configurable (most users shouldn't really care too much what kind of tag it is though)
            return lightweightTag || finalWalk.parseTag(tagRef.getObjectId()).getObject().getId().equals(headId); // or normal tag
          } catch (IOException e) {
            return false;
          }
        }
      });

      Collection tags = Collections2.transform(tagsForHeadCommit, new Function() {
        @Override public String apply(Ref input) {
          return input.getName().replaceAll("refs/tags/", "");
        }
      });

      return tags;
    } finally {
      if (walk != null) {
        walk.dispose();
      }
    }
  }

  public String getClosestTagName(@NotNull Repository repo){
    Map> map = getClosestTagAsMap(repo);
    for(Map.Entry> entry : map.entrySet()){
      return trimFullTagName(entry.getValue().get(0).tagName);
    }
    return "";
  }

  public String getClosestTagCommitCount(@NotNull Repository repo, RevCommit headCommit){
    HashMap> map = transformRevTagsMapToDateSortedTagNames(getClosestTagAsMap(repo));
    ObjectId obj = (ObjectId) map.keySet().toArray()[0];
    
    RevWalk walk = new RevWalk(repo);
    RevCommit commit = walk.lookupCommit(obj);
    walk.dispose();
    
    int distance = distanceBetween(repo, headCommit, commit);
    return String.valueOf(distance);
  }

  private Map> getClosestTagAsMap(@NotNull Repository repo){
    Map> mapWithClosestTagOnly = new HashMap<>();
    String matchPattern = ".*";
    Map> commitIdsToTags = getCommitIdsToTags(repo, true, matchPattern);
    LinkedHashMap> sortedCommitIdsToTags = sortByDatedRevTag(commitIdsToTags);

    for (Map.Entry> entry: sortedCommitIdsToTags.entrySet()){
      mapWithClosestTagOnly.put(entry.getKey(), entry.getValue());
      break;
    }

    return mapWithClosestTagOnly;
  }

  private LinkedHashMap> sortByDatedRevTag(Map> map) {
    List>> list = new ArrayList<>(map.entrySet());

    Collections.sort(list, new Comparator>>() {
      public int compare(Map.Entry> m1, Map.Entry> m2) {
        // we need to sort the DatedRevTags to a commit first, otherwise we may get problems when we have two tags for the same commit
        Collections.sort(m1.getValue(), datedRevTagComparator());
        Collections.sort(m2.getValue(), datedRevTagComparator());

        DatedRevTag datedRevTag1 = m1.getValue().get(0);
        DatedRevTag datedRevTag2 = m2.getValue().get(0);
        return datedRevTagComparator().compare(datedRevTag1,datedRevTag2);
      }
    });

    LinkedHashMap> result = new LinkedHashMap<>();
    for (Map.Entry> entry : list) {
      result.put(entry.getKey(), entry.getValue());
    }
    return result;
  }

  protected Map> getCommitIdsToTags(@NotNull Repository repo, boolean includeLightweightTags, String matchPattern){
    Map> commitIdsToTags = new HashMap<>();

    try (RevWalk walk = new RevWalk(repo)) {
      walk.markStart(walk.parseCommit(repo.resolve("HEAD")));

      List tagRefs = Git.wrap(repo).tagList().call();
      Pattern regex = Pattern.compile(matchPattern);
      log.info("Tag refs [{}]", tagRefs);

      for (Ref tagRef : tagRefs) {
        walk.reset();
        String name = tagRef.getName();
        if (!regex.matcher(name).matches()) {
          log.info("Skipping tagRef with name [{}] as it doesn't match [{}]", name, matchPattern);
          continue;
        }
        ObjectId resolvedCommitId = repo.resolve(name);

        // TODO that's a bit of a hack...
        try {
          final RevTag revTag = walk.parseTag(resolvedCommitId);
          ObjectId taggedCommitId = revTag.getObject().getId();
          log.info("Resolved tag [{}] [{}], points at [{}] ", revTag.getTagName(), revTag.getTaggerIdent(), taggedCommitId);

          // sometimes a tag, may point to another tag, so we need to unpack it
          while (isTagId(taggedCommitId)) {
            taggedCommitId = walk.parseTag(taggedCommitId).getObject().getId();
          }

          if (commitIdsToTags.containsKey(taggedCommitId)) {
            commitIdsToTags.get(taggedCommitId).add(new DatedRevTag(revTag));
          } else {
            commitIdsToTags.put(taggedCommitId, new ArrayList<>(Collections.singletonList(new DatedRevTag(revTag))));
          }

        } catch (IncorrectObjectTypeException ex) {
          // it's an lightweight tag! (yeah, really)
          if (includeLightweightTags) {
            // --tags means "include lightweight tags"
            log.info("Including lightweight tag [{}]", name);

            DatedRevTag datedRevTag = new DatedRevTag(resolvedCommitId, name);

            if (commitIdsToTags.containsKey(resolvedCommitId)) {
              commitIdsToTags.get(resolvedCommitId).add(datedRevTag);
            } else {
              commitIdsToTags.put(resolvedCommitId, new ArrayList<>(Collections.singletonList(datedRevTag)));
            }
          }
        } catch (Exception ignored) {
          log.info("Failed while parsing [{}] -- ", tagRef, ignored);
        }
      }

      for (Map.Entry> entry : commitIdsToTags.entrySet()) {
        log.info("key [{}], tags => [{}] ", entry.getKey(), entry.getValue());
      }
      return commitIdsToTags;
    } catch (Exception e) {
      log.info("Unable to locate tags", e);
    }
    return Collections.emptyMap();
  }

  /** Checks if the given object id resolved to a tag object */
  private boolean isTagId(ObjectId objectId) {
    return objectId.toString().startsWith("tag ");
  }

  protected HashMap> transformRevTagsMapToDateSortedTagNames(Map> commitIdsToTags) {
    HashMap> commitIdsToTagNames = new HashMap<>();
    for (Map.Entry> objectIdListEntry : commitIdsToTags.entrySet()) {
      List tagNames = transformRevTagsMapEntryToDateSortedTagNames(objectIdListEntry);

      commitIdsToTagNames.put(objectIdListEntry.getKey(), tagNames);
    }
    return commitIdsToTagNames;
  }

  private List transformRevTagsMapEntryToDateSortedTagNames(Map.Entry> objectIdListEntry) {
    List tags = objectIdListEntry.getValue();

    List newTags = new ArrayList<>(tags);
    Collections.sort(newTags, datedRevTagComparator());

    List tagNames = Lists.transform(newTags, new Function() {
      @Override
      public String apply(DatedRevTag input) {
        return trimFullTagName(input.tagName);
      }
    });
    return tagNames;
  }

  private Comparator datedRevTagComparator() {
    return new Comparator() {
        @Override
        public int compare(DatedRevTag revTag, DatedRevTag revTag2) {
          return revTag2.date.compareTo(revTag.date);
        }
      };
  }

  @VisibleForTesting
  protected String trimFullTagName(@NotNull String tagName) {
    return tagName.replaceFirst("refs/tags/", "");
  }

  public List findCommitsUntilSomeTag(Repository repo, RevCommit head, @NotNull Map> tagObjectIdToName) {
    try (RevWalk revWalk = new RevWalk(repo)) {
      revWalk.markStart(head);

      for (RevCommit commit : revWalk) {
        ObjectId objId = commit.getId();
        if (tagObjectIdToName.size() > 0) {
          List maybeList = tagObjectIdToName.get(objId);
          if (maybeList != null && maybeList.get(0) != null) {
            return Collections.singletonList(commit);
          }
        }
      }

      return Collections.emptyList();
    } catch (Exception e) {
      throw new RuntimeException("Unable to find commits until some tag", e);
    }
  }

   /**
   * Calculates the distance (number of commits) between the given parent and child commits.
   *
   * @return distance (number of commits) between the given commits
   * @see mdonoughe/jgit-describe/blob/master/src/org/mdonoughe/JGitDescribeTask.java
   */
  protected int distanceBetween(@NotNull Repository repo, @NotNull RevCommit child, @NotNull RevCommit parent) {
    try (RevWalk revWalk = new RevWalk(repo)) {
      revWalk.markStart(child);

      Set seena = new HashSet<>();
      Set seenb = new HashSet<>();
      Queue q = new ArrayDeque<>();

      q.add(revWalk.parseCommit(child));
      int distance = 0;
      ObjectId parentId = parent.getId();

      while (q.size() > 0) {
        RevCommit commit = q.remove();
        ObjectId commitId = commit.getId();

        if (seena.contains(commitId)) {
          continue;
        }
        seena.add(commitId);

        if (parentId.equals(commitId)) {
          // don't consider commits that are included in this commit
          seeAllParents(revWalk, commit, seenb);
          // remove things we shouldn't have included
          for (ObjectId oid : seenb) {
            if (seena.contains(oid)) {
              distance--;
            }
          }
          seena.addAll(seenb);
          continue;
        }

        for (ObjectId oid : commit.getParents()) {
          if (!seena.contains(oid)) {
            q.add(revWalk.parseCommit(oid));
          }
        }
        distance++;
      }
      return distance;
    } catch (Exception e) {
      throw new RuntimeException(String.format("Unable to calculate distance between [%s] and [%s]", child, parent), e);
    }
  }

  private void seeAllParents(@NotNull RevWalk revWalk, RevCommit child, @NotNull Set seen) throws IOException {
    Queue q = new ArrayDeque<>();
    q.add(child);

    while (q.size() > 0) {
      RevCommit commit = q.remove();
      for (ObjectId oid : commit.getParents()) {
        if (seen.contains(oid)) {
          continue;
        }
        seen.add(oid);
        q.add(revWalk.parseCommit(oid));
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy