/* * Copyright (C) 2008-2018, Robin Rosenberg * Copyright (C) 2008, Shawn O. Pearce and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.revplot; import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.eclipse.jgit.lib.Constants.R_REMOTES; import static org.eclipse.jgit.lib.Constants.R_TAGS; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevSort; import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; /** * Specialized RevWalk for visualization of a commit graph. */ public class PlotWalk extends RevWalk { private Map> additionalRefMap; private Map> reverseRefMap; private Repository repository; @Override public void dispose() { super.dispose(); if (reverseRefMap != null) { reverseRefMap.clear(); reverseRefMap = null; } if (additionalRefMap != null) { additionalRefMap.clear(); additionalRefMap = null; } repository = null; } /** * Create a new revision walker for a given repository. * * @param repo * the repository the walker will obtain data from. */ public PlotWalk(Repository repo) { super(repo); super.sort(RevSort.TOPO, true); additionalRefMap = new HashMap<>(); repository = repo; } /** * Add additional refs to the walk * * @param refs * additional refs * @throws java.io.IOException * if an IO error occurred */ public void addAdditionalRefs(Iterable refs) throws IOException { for (Ref ref : refs) { Set set = additionalRefMap.get(ref.getObjectId()); if (set == null) set = Collections.singleton(ref); else { set = new HashSet<>(set); set.add(ref); } additionalRefMap.put(ref.getObjectId(), set); } } @Override public void sort(RevSort s, boolean use) { if (s == RevSort.TOPO && !use) throw new IllegalArgumentException(JGitText.get().topologicalSortRequired); super.sort(s, use); } @Override protected RevCommit createCommit(AnyObjectId id) { return new PlotCommit(id); } @Override public RevCommit next() throws MissingObjectException, IncorrectObjectTypeException, IOException { PlotCommit pc = (PlotCommit) super.next(); if (pc != null) pc.refs = getRefs(pc); return pc; } private Ref[] getRefs(AnyObjectId commitId) throws IOException { if (reverseRefMap == null) { reverseRefMap = repository.getAllRefsByPeeledObjectId(); for (Map.Entry> entry : additionalRefMap .entrySet()) { Set set = reverseRefMap.get(entry.getKey()); Set additional = entry.getValue(); if (set != null) { if (additional.size() == 1) { // It's an unmodifiable singleton set... additional = new HashSet<>(additional); } additional.addAll(set); } reverseRefMap.put(entry.getKey(), additional); } additionalRefMap.clear(); additionalRefMap = null; } Collection list = reverseRefMap.get(commitId); if (list == null) { return PlotCommit.NO_REFS; } Ref[] tags = list.toArray(new Ref[0]); Arrays.sort(tags, new PlotRefComparator()); return tags; } class PlotRefComparator implements Comparator { @Override public int compare(Ref o1, Ref o2) { try { RevObject obj1 = parseAny(o1.getObjectId()); RevObject obj2 = parseAny(o2.getObjectId()); long t1 = timeof(obj1); long t2 = timeof(obj2); if (t1 > t2) return -1; if (t1 < t2) return 1; } catch (IOException e) { // ignore } int cmp = kind(o1) - kind(o2); if (cmp == 0) cmp = o1.getName().compareTo(o2.getName()); return cmp; } long timeof(RevObject o) { if (o instanceof RevCommit) { return ((RevCommit) o).getCommitTime() * 1000L; } if (o instanceof RevTag) { RevTag tag = (RevTag) o; try { parseBody(tag); } catch (IOException e) { return 0; } PersonIdent who = tag.getTaggerIdent(); return who != null ? who.getWhenAsInstant().toEpochMilli() : 0; } return 0; } int kind(Ref r) { if (r.getName().startsWith(R_TAGS)) return 0; if (r.getName().startsWith(R_HEADS)) return 1; if (r.getName().startsWith(R_REMOTES)) return 2; return 3; } } }