Search in sources :

Example 1 with RevFlagSet

use of org.eclipse.jgit.revwalk.RevFlagSet in project egit by eclipse.

the class GitCommitsModelCache method build.

/**
 * Scans given {@code repo} and build list of commits between two given
 * RevCommit objectId's. Each commit contains list of changed resources
 *
 * @param repo
 *            repository that should be scanned
 * @param srcId
 *            commit id that is considered the "local" version (e.g. from
 *            master)
 * @param dstId
 *            commit id that is considered the "remote" version (e.g. from
 *            origin/master)
 * @param pathFilter
 *            path filter definition or {@code null} when all paths should
 *            be included
 * @return list of {@link Commit} object's between {@code srcId} and
 *         {@code dstId}
 * @throws IOException
 */
public static List<Commit> build(Repository repo, ObjectId srcId, ObjectId dstId, TreeFilter pathFilter) throws IOException {
    if (dstId.equals(srcId))
        return new ArrayList<Commit>(0);
    try (RevWalk rw = new RevWalk(repo)) {
        // $NON-NLS-1$
        final RevFlag localFlag = rw.newFlag("local");
        // $NON-NLS-1$
        final RevFlag remoteFlag = rw.newFlag("remote");
        final RevFlagSet allFlags = new RevFlagSet();
        allFlags.add(localFlag);
        allFlags.add(remoteFlag);
        rw.carry(allFlags);
        RevCommit srcCommit = rw.parseCommit(srcId);
        srcCommit.add(localFlag);
        rw.markStart(srcCommit);
        // free not needed resources
        srcCommit = null;
        RevCommit dstCommit = rw.parseCommit(dstId);
        dstCommit.add(remoteFlag);
        rw.markStart(dstCommit);
        // free not needed resources
        dstCommit = null;
        if (pathFilter != null)
            rw.setTreeFilter(pathFilter);
        List<Commit> result = new ArrayList<Commit>();
        for (RevCommit revCommit : rw) {
            if (revCommit.hasAll(allFlags))
                break;
            Commit commit = new Commit();
            commit.shortMessage = revCommit.getShortMessage();
            commit.commitId = AbbreviatedObjectId.fromObjectId(revCommit);
            commit.authorName = revCommit.getAuthorIdent().getName();
            commit.committerName = revCommit.getCommitterIdent().getName();
            commit.commitDate = revCommit.getAuthorIdent().getWhen();
            RevCommit parentCommit = getParentCommit(revCommit);
            if (revCommit.has(localFlag))
                // Outgoing
                commit.direction = RIGHT;
            else if (revCommit.has(remoteFlag))
                // Incoming
                commit.direction = LEFT;
            else
                throw new GitCommitsModelDirectionException();
            commit.children = getChangedObjects(repo, revCommit, parentCommit, pathFilter, commit.direction);
            if (commit.children != null)
                result.add(commit);
        }
        rw.dispose();
        return result;
    }
}
Also used : RevCommit(org.eclipse.jgit.revwalk.RevCommit) RevFlagSet(org.eclipse.jgit.revwalk.RevFlagSet) RevFlag(org.eclipse.jgit.revwalk.RevFlag) ArrayList(java.util.ArrayList) RevWalk(org.eclipse.jgit.revwalk.RevWalk) RevCommit(org.eclipse.jgit.revwalk.RevCommit)

Example 2 with RevFlagSet

use of org.eclipse.jgit.revwalk.RevFlagSet in project git-client-plugin by jenkinsci.

the class JGitAPIImpl method describe.

/**
 * {@inheritDoc}
 *
 * This implementation is based on my reading of the cgit source code at https://github.com/git/git/blob/master/builtin/describe.c
 *
 * <p>
 * The basic structure of the algorithm is as follows. We walk the commit graph,
 * find tags, and mark commits that are reachable from those tags. The marking
 * uses flags given by JGit, so there's a fairly small upper bound in the number of tags
 * we can keep track of.
 *
 * <p>
 * As we walk commits, we count commits that each tag doesn't contain.
 * We call it "depth", following the variable name in C Git.
 * As we walk further and find enough tags, we go into wind-down mode and only walk
 * to the point of accurately determining all the depths.
 */
@Override
public String describe(String tip) throws GitException, InterruptedException {
    try (Repository repo = getRepository()) {
        final ObjectReader or = repo.newObjectReader();
        // How to dispose of this ?
        final RevWalk w = new RevWalk(or);
        w.setRetainBody(false);
        Map<ObjectId, Ref> tags = new HashMap<>();
        for (Ref r : repo.getTags().values()) {
            ObjectId key = repo.peel(r).getPeeledObjectId();
            if (key == null)
                key = r.getObjectId();
            tags.put(key, r);
        }
        // combined flags of all the Candidate instances
        final RevFlagSet allFlags = new RevFlagSet();
        /**
         * Tracks the depth of each tag as we find them.
         */
        class Candidate {

            final Ref tag;

            final RevFlag flag;

            /**
             * This field number of commits that are reachable from the tip but
             * not reachable from the tag.
             */
            int depth;

            Candidate(RevCommit commit, Ref tag) {
                this.tag = tag;
                this.flag = w.newFlag(tag.getName());
                // we'll mark all the nodes reachable from this tag accordingly
                allFlags.add(flag);
                w.carry(flag);
                commit.add(flag);
                commit.carry(flag);
            }

            /**
             * Does this tag contains the given commit?
             */
            public boolean reaches(RevCommit c) {
                return c.has(flag);
            }

            public String describe(ObjectId tip) throws IOException {
                return String.format("%s-%d-g%s", tag.getName().substring(R_TAGS.length()), depth, or.abbreviate(tip).name());
            }
        }
        // all the candidates we find
        List<Candidate> candidates = new ArrayList<>();
        ObjectId tipId = repo.resolve(tip);
        Ref lucky = tags.get(tipId);
        if (lucky != null)
            return lucky.getName().substring(R_TAGS.length());
        w.markStart(w.parseCommit(tipId));
        int maxCandidates = 10;
        // commit seen thus far
        int seen = 0;
        RevCommit c;
        while ((c = w.next()) != null) {
            if (!c.hasAny(allFlags)) {
                // if a tag already dominates this commit,
                // then there's no point in picking a tag on this commit
                // since the one that dominates it is always more preferable
                Ref t = tags.get(c);
                if (t != null) {
                    Candidate cd = new Candidate(c, t);
                    candidates.add(cd);
                    cd.depth = seen;
                }
            }
            // it counts toward the total depth.
            for (Candidate cd : candidates) {
                if (!cd.reaches(c)) {
                    cd.depth++;
                }
            }
            // all tags even if we wanted to.
            if (candidates.size() >= maxCandidates)
                break;
            // TODO: if all the commits in the queue of RevWalk has allFlags
            // there's no point in continuing search as we'll not discover any more
            // tags. But RevWalk doesn't expose this.
            seen++;
        }
        // but we still need to count all the depths correctly.
        while ((c = w.next()) != null) {
            if (c.hasAll(allFlags)) {
                // no point in visiting further from here, so cut the search here
                for (RevCommit p : c.getParents()) p.add(RevFlag.SEEN);
            } else {
                for (Candidate cd : candidates) {
                    if (!cd.reaches(c)) {
                        cd.depth++;
                    }
                }
            }
        }
        if (candidates.isEmpty())
            throw new GitException("No tags can describe " + tip);
        // if all the nodes are dominated by all the tags, the walk stops
        candidates.sort(Comparator.comparingInt((Candidate o) -> o.depth));
        return candidates.get(0).describe(tipId);
    } catch (IOException e) {
        throw new GitException(e);
    }
}
Also used : RevFlagSet(org.eclipse.jgit.revwalk.RevFlagSet) ObjectId(org.eclipse.jgit.lib.ObjectId) HashMap(java.util.HashMap) GitException(hudson.plugins.git.GitException) ArrayList(java.util.ArrayList) IOException(java.io.IOException) RevWalk(org.eclipse.jgit.revwalk.RevWalk) FileRepository(org.eclipse.jgit.internal.storage.file.FileRepository) Repository(org.eclipse.jgit.lib.Repository) Ref(org.eclipse.jgit.lib.Ref) RevFlag(org.eclipse.jgit.revwalk.RevFlag) ObjectReader(org.eclipse.jgit.lib.ObjectReader) RevCommit(org.eclipse.jgit.revwalk.RevCommit)

Aggregations

ArrayList (java.util.ArrayList)2 RevCommit (org.eclipse.jgit.revwalk.RevCommit)2 RevFlag (org.eclipse.jgit.revwalk.RevFlag)2 RevFlagSet (org.eclipse.jgit.revwalk.RevFlagSet)2 RevWalk (org.eclipse.jgit.revwalk.RevWalk)2 GitException (hudson.plugins.git.GitException)1 IOException (java.io.IOException)1 HashMap (java.util.HashMap)1 FileRepository (org.eclipse.jgit.internal.storage.file.FileRepository)1 ObjectId (org.eclipse.jgit.lib.ObjectId)1 ObjectReader (org.eclipse.jgit.lib.ObjectReader)1 Ref (org.eclipse.jgit.lib.Ref)1 Repository (org.eclipse.jgit.lib.Repository)1