1f045a68aSKohsuke Kawaguchi /* 25c5f7c6bSMatthias Sohn * Copyright (C) 2013, CloudBees, Inc. and others 3f045a68aSKohsuke Kawaguchi * 45c5f7c6bSMatthias Sohn * This program and the accompanying materials are made available under the 55c5f7c6bSMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 65c5f7c6bSMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 7f045a68aSKohsuke Kawaguchi * 85c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 9f045a68aSKohsuke Kawaguchi */ 10f045a68aSKohsuke Kawaguchi package org.eclipse.jgit.api; 11f045a68aSKohsuke Kawaguchi 12*276fcb2aSJason Yeo import static org.eclipse.jgit.lib.Constants.R_REFS; 130cf902e4SShawn Pearce import static org.eclipse.jgit.lib.Constants.R_TAGS; 14f045a68aSKohsuke Kawaguchi 15f045a68aSKohsuke Kawaguchi import java.io.IOException; 16f045a68aSKohsuke Kawaguchi import java.text.MessageFormat; 17f045a68aSKohsuke Kawaguchi import java.util.ArrayList; 18060f3699SOliver Lockwood import java.util.Collection; 19f045a68aSKohsuke Kawaguchi import java.util.Collections; 20f045a68aSKohsuke Kawaguchi import java.util.Comparator; 21f9de9175SHåvard Wall import java.util.Date; 22f045a68aSKohsuke Kawaguchi import java.util.List; 23f045a68aSKohsuke Kawaguchi import java.util.Map; 24060f3699SOliver Lockwood import java.util.Optional; 25060f3699SOliver Lockwood import java.util.stream.Collectors; 26df626364SMatthias Sohn import java.util.stream.Stream; 27f045a68aSKohsuke Kawaguchi 280cf902e4SShawn Pearce import org.eclipse.jgit.api.errors.GitAPIException; 290cf902e4SShawn Pearce import org.eclipse.jgit.api.errors.JGitInternalException; 300cf902e4SShawn Pearce import org.eclipse.jgit.api.errors.RefNotFoundException; 310cf902e4SShawn Pearce import org.eclipse.jgit.errors.IncorrectObjectTypeException; 32af0867cbSOliver Lockwood import org.eclipse.jgit.errors.InvalidPatternException; 330cf902e4SShawn Pearce import org.eclipse.jgit.errors.MissingObjectException; 3410ab407fSThomas Wolf import org.eclipse.jgit.fnmatch.FileNameMatcher; 350cf902e4SShawn Pearce import org.eclipse.jgit.internal.JGitText; 360cf902e4SShawn Pearce import org.eclipse.jgit.lib.Constants; 370cf902e4SShawn Pearce import org.eclipse.jgit.lib.ObjectId; 380cf902e4SShawn Pearce import org.eclipse.jgit.lib.Ref; 390cf902e4SShawn Pearce import org.eclipse.jgit.lib.Repository; 400cf902e4SShawn Pearce import org.eclipse.jgit.revwalk.RevCommit; 410cf902e4SShawn Pearce import org.eclipse.jgit.revwalk.RevFlag; 420cf902e4SShawn Pearce import org.eclipse.jgit.revwalk.RevFlagSet; 43f9de9175SHåvard Wall import org.eclipse.jgit.revwalk.RevTag; 440cf902e4SShawn Pearce import org.eclipse.jgit.revwalk.RevWalk; 45f045a68aSKohsuke Kawaguchi 46f045a68aSKohsuke Kawaguchi /** 47f045a68aSKohsuke Kawaguchi * Given a commit, show the most recent tag that is reachable from a commit. 48f045a68aSKohsuke Kawaguchi * 4972c76185SMatthias Sohn * @since 3.2 50f045a68aSKohsuke Kawaguchi */ 51f045a68aSKohsuke Kawaguchi public class DescribeCommand extends GitCommand<String> { 52f045a68aSKohsuke Kawaguchi private final RevWalk w; 53f045a68aSKohsuke Kawaguchi 54f045a68aSKohsuke Kawaguchi /** 55f045a68aSKohsuke Kawaguchi * Commit to describe. 56f045a68aSKohsuke Kawaguchi */ 57f045a68aSKohsuke Kawaguchi private RevCommit target; 58f045a68aSKohsuke Kawaguchi 59f045a68aSKohsuke Kawaguchi /** 60f045a68aSKohsuke Kawaguchi * How many tags we'll consider as candidates. 61f045a68aSKohsuke Kawaguchi * This can only go up to the number of flags JGit can support in a walk, 62f045a68aSKohsuke Kawaguchi * which is 24. 63f045a68aSKohsuke Kawaguchi */ 64f045a68aSKohsuke Kawaguchi private int maxCandidates = 10; 65f045a68aSKohsuke Kawaguchi 66f045a68aSKohsuke Kawaguchi /** 670e3ddea1SChristian Halstrick * Whether to always use long output format or not. 680e3ddea1SChristian Halstrick */ 690e3ddea1SChristian Halstrick private boolean longDesc; 700e3ddea1SChristian Halstrick 710e3ddea1SChristian Halstrick /** 72d9e16538SSebastian Schuberth * Pattern matchers to be applied to tags under consideration. 73af0867cbSOliver Lockwood */ 7410ab407fSThomas Wolf private List<FileNameMatcher> matchers = new ArrayList<>(); 75af0867cbSOliver Lockwood 76af0867cbSOliver Lockwood /** 77*276fcb2aSJason Yeo * Whether to use all refs in the refs/ namespace 78*276fcb2aSJason Yeo */ 79*276fcb2aSJason Yeo private boolean useAll; 80*276fcb2aSJason Yeo 81*276fcb2aSJason Yeo /** 82d9e16538SSebastian Schuberth * Whether to use all tags (incl. lightweight) or not. 835429d1a0SMarcel Trautwein */ 84afcb3a8cSSebastian Schuberth private boolean useTags; 855429d1a0SMarcel Trautwein 865429d1a0SMarcel Trautwein /** 87237c76fdSSebastian Schuberth * Whether to show a uniquely abbreviated commit hash as a fallback or not. 88237c76fdSSebastian Schuberth */ 89237c76fdSSebastian Schuberth private boolean always; 90237c76fdSSebastian Schuberth 91237c76fdSSebastian Schuberth /** 920210e0e2SMatthias Sohn * Constructor for DescribeCommand. 93f045a68aSKohsuke Kawaguchi * 94f045a68aSKohsuke Kawaguchi * @param repo 950210e0e2SMatthias Sohn * the {@link org.eclipse.jgit.lib.Repository} 96f045a68aSKohsuke Kawaguchi */ DescribeCommand(Repository repo)97f045a68aSKohsuke Kawaguchi protected DescribeCommand(Repository repo) { 98f045a68aSKohsuke Kawaguchi super(repo); 99f045a68aSKohsuke Kawaguchi w = new RevWalk(repo); 100f045a68aSKohsuke Kawaguchi w.setRetainBody(false); 101f045a68aSKohsuke Kawaguchi } 102f045a68aSKohsuke Kawaguchi 103f045a68aSKohsuke Kawaguchi /** 104f045a68aSKohsuke Kawaguchi * Sets the commit to be described. 105f045a68aSKohsuke Kawaguchi * 106f045a68aSKohsuke Kawaguchi * @param target 107f045a68aSKohsuke Kawaguchi * A non-null object ID to be described. 108f045a68aSKohsuke Kawaguchi * @return {@code this} 109f045a68aSKohsuke Kawaguchi * @throws MissingObjectException 110f045a68aSKohsuke Kawaguchi * the supplied commit does not exist. 111f045a68aSKohsuke Kawaguchi * @throws IncorrectObjectTypeException 112f045a68aSKohsuke Kawaguchi * the supplied id is not a commit or an annotated tag. 1130210e0e2SMatthias Sohn * @throws java.io.IOException 114f045a68aSKohsuke Kawaguchi * a pack file or loose object could not be read. 115f045a68aSKohsuke Kawaguchi */ setTarget(ObjectId target)1163c2c8b47SMatthias Sohn public DescribeCommand setTarget(ObjectId target) throws IOException { 117f045a68aSKohsuke Kawaguchi this.target = w.parseCommit(target); 118f045a68aSKohsuke Kawaguchi return this; 119f045a68aSKohsuke Kawaguchi } 120f045a68aSKohsuke Kawaguchi 121f045a68aSKohsuke Kawaguchi /** 122f045a68aSKohsuke Kawaguchi * Sets the commit to be described. 123f045a68aSKohsuke Kawaguchi * 124f045a68aSKohsuke Kawaguchi * @param rev 1250210e0e2SMatthias Sohn * Commit ID, tag, branch, ref, etc. See 1260210e0e2SMatthias Sohn * {@link org.eclipse.jgit.lib.Repository#resolve(String)} for 1270210e0e2SMatthias Sohn * allowed syntax. 128f045a68aSKohsuke Kawaguchi * @return {@code this} 129f045a68aSKohsuke Kawaguchi * @throws IncorrectObjectTypeException 130f045a68aSKohsuke Kawaguchi * the supplied id is not a commit or an annotated tag. 1310210e0e2SMatthias Sohn * @throws org.eclipse.jgit.api.errors.RefNotFoundException 132f045a68aSKohsuke Kawaguchi * the given rev didn't resolve to any object. 1330210e0e2SMatthias Sohn * @throws java.io.IOException 134f045a68aSKohsuke Kawaguchi * a pack file or loose object could not be read. 135f045a68aSKohsuke Kawaguchi */ setTarget(String rev)1363c2c8b47SMatthias Sohn public DescribeCommand setTarget(String rev) throws IOException, 1373c2c8b47SMatthias Sohn RefNotFoundException { 138f045a68aSKohsuke Kawaguchi ObjectId id = repo.resolve(rev); 139f045a68aSKohsuke Kawaguchi if (id == null) 140f045a68aSKohsuke Kawaguchi throw new RefNotFoundException(MessageFormat.format(JGitText.get().refNotResolved, rev)); 141f045a68aSKohsuke Kawaguchi return setTarget(id); 142f045a68aSKohsuke Kawaguchi } 143f045a68aSKohsuke Kawaguchi 144f045a68aSKohsuke Kawaguchi /** 1450e3ddea1SChristian Halstrick * Determine whether always to use the long format or not. When set to 1460e3ddea1SChristian Halstrick * <code>true</code> the long format is used even the commit matches a tag. 1470e3ddea1SChristian Halstrick * 1480e3ddea1SChristian Halstrick * @param longDesc 1490e3ddea1SChristian Halstrick * <code>true</code> if always the long format should be used. 1500e3ddea1SChristian Halstrick * @return {@code this} 1510e3ddea1SChristian Halstrick * @see <a 1520e3ddea1SChristian Halstrick * href="https://www.kernel.org/pub/software/scm/git/docs/git-describe.html" 1530e3ddea1SChristian Halstrick * >Git documentation about describe</a> 1540e3ddea1SChristian Halstrick * @since 4.0 1550e3ddea1SChristian Halstrick */ setLong(boolean longDesc)1560e3ddea1SChristian Halstrick public DescribeCommand setLong(boolean longDesc) { 1570e3ddea1SChristian Halstrick this.longDesc = longDesc; 1580e3ddea1SChristian Halstrick return this; 1590e3ddea1SChristian Halstrick } 1600e3ddea1SChristian Halstrick 1615429d1a0SMarcel Trautwein /** 162*276fcb2aSJason Yeo * Instead of using only the annotated tags, use any ref found in refs/ 163*276fcb2aSJason Yeo * namespace. This option enables matching any known branch, 164*276fcb2aSJason Yeo * remote-tracking branch, or lightweight tag. 165*276fcb2aSJason Yeo * 166*276fcb2aSJason Yeo * @param all 167*276fcb2aSJason Yeo * <code>true</code> enables matching any ref found in refs/ 168*276fcb2aSJason Yeo * like setting option --all in c git 169*276fcb2aSJason Yeo * @return {@code this} 170*276fcb2aSJason Yeo * @since 5.10 171*276fcb2aSJason Yeo */ setAll(boolean all)172*276fcb2aSJason Yeo public DescribeCommand setAll(boolean all) { 173*276fcb2aSJason Yeo this.useAll = all; 174*276fcb2aSJason Yeo return this; 175*276fcb2aSJason Yeo } 176*276fcb2aSJason Yeo 177*276fcb2aSJason Yeo /** 1785429d1a0SMarcel Trautwein * Instead of using only the annotated tags, use any tag found in refs/tags 1795429d1a0SMarcel Trautwein * namespace. This option enables matching lightweight (non-annotated) tags 1805429d1a0SMarcel Trautwein * or not. 1815429d1a0SMarcel Trautwein * 1825429d1a0SMarcel Trautwein * @param tags 1835429d1a0SMarcel Trautwein * <code>true</code> enables matching lightweight (non-annotated) 1845429d1a0SMarcel Trautwein * tags like setting option --tags in c git 1855429d1a0SMarcel Trautwein * @return {@code this} 1865429d1a0SMarcel Trautwein * @since 5.0 1875429d1a0SMarcel Trautwein */ setTags(boolean tags)1885429d1a0SMarcel Trautwein public DescribeCommand setTags(boolean tags) { 1895429d1a0SMarcel Trautwein this.useTags = tags; 1905429d1a0SMarcel Trautwein return this; 1915429d1a0SMarcel Trautwein } 1925429d1a0SMarcel Trautwein 193237c76fdSSebastian Schuberth /** 194237c76fdSSebastian Schuberth * Always describe the commit by eventually falling back to a uniquely 195237c76fdSSebastian Schuberth * abbreviated commit hash if no other name matches. 196237c76fdSSebastian Schuberth * 197237c76fdSSebastian Schuberth * @param always 198237c76fdSSebastian Schuberth * <code>true</code> enables falling back to a uniquely 199237c76fdSSebastian Schuberth * abbreviated commit hash 200237c76fdSSebastian Schuberth * @return {@code this} 201237c76fdSSebastian Schuberth * @since 5.4 202237c76fdSSebastian Schuberth */ setAlways(boolean always)203237c76fdSSebastian Schuberth public DescribeCommand setAlways(boolean always) { 204237c76fdSSebastian Schuberth this.always = always; 205237c76fdSSebastian Schuberth return this; 206237c76fdSSebastian Schuberth } 207237c76fdSSebastian Schuberth longDescription(Ref tag, int depth, ObjectId tip)2080e3ddea1SChristian Halstrick private String longDescription(Ref tag, int depth, ObjectId tip) 2090e3ddea1SChristian Halstrick throws IOException { 2100e3ddea1SChristian Halstrick return String.format( 211*276fcb2aSJason Yeo "%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$ 2120e3ddea1SChristian Halstrick Integer.valueOf(depth), w.getObjectReader().abbreviate(tip) 2130e3ddea1SChristian Halstrick .name()); 2140e3ddea1SChristian Halstrick } 2150e3ddea1SChristian Halstrick 2160e3ddea1SChristian Halstrick /** 2170210e0e2SMatthias Sohn * Sets one or more {@code glob(7)} patterns that tags must match to be 2180210e0e2SMatthias Sohn * considered. If multiple patterns are provided, tags only need match one 2190210e0e2SMatthias Sohn * of them. 220af0867cbSOliver Lockwood * 2210210e0e2SMatthias Sohn * @param patterns 2220210e0e2SMatthias Sohn * the {@code glob(7)} pattern or patterns 223af0867cbSOliver Lockwood * @return {@code this} 2240210e0e2SMatthias Sohn * @throws org.eclipse.jgit.errors.InvalidPatternException 2250210e0e2SMatthias Sohn * if the pattern passed in was invalid. 2260210e0e2SMatthias Sohn * @see <a href= 2270210e0e2SMatthias Sohn * "https://www.kernel.org/pub/software/scm/git/docs/git-describe.html" 228af0867cbSOliver Lockwood * >Git documentation about describe</a> 229af0867cbSOliver Lockwood * @since 4.9 230af0867cbSOliver Lockwood */ setMatch(String... patterns)231af0867cbSOliver Lockwood public DescribeCommand setMatch(String... patterns) throws InvalidPatternException { 232af0867cbSOliver Lockwood for (String p : patterns) { 23310ab407fSThomas Wolf matchers.add(new FileNameMatcher(p, null)); 234af0867cbSOliver Lockwood } 235af0867cbSOliver Lockwood return this; 236af0867cbSOliver Lockwood } 237af0867cbSOliver Lockwood 238f9de9175SHåvard Wall private final Comparator<Ref> TAG_TIE_BREAKER = new Comparator<Ref>() { 239f9de9175SHåvard Wall 240f9de9175SHåvard Wall @Override 241f9de9175SHåvard Wall public int compare(Ref o1, Ref o2) { 242f9de9175SHåvard Wall try { 243f9de9175SHåvard Wall return tagDate(o2).compareTo(tagDate(o1)); 244f9de9175SHåvard Wall } catch (IOException e) { 245f9de9175SHåvard Wall return 0; 246f9de9175SHåvard Wall } 247f9de9175SHåvard Wall } 248f9de9175SHåvard Wall 249f9de9175SHåvard Wall private Date tagDate(Ref tag) throws IOException { 250f9de9175SHåvard Wall RevTag t = w.parseTag(tag.getObjectId()); 251f9de9175SHåvard Wall w.parseBody(t); 252f9de9175SHåvard Wall return t.getTaggerIdent().getWhen(); 253f9de9175SHåvard Wall } 254f9de9175SHåvard Wall }; 255f9de9175SHåvard Wall getBestMatch(List<Ref> tags)256060f3699SOliver Lockwood private Optional<Ref> getBestMatch(List<Ref> tags) { 2576a4c77e6SCarsten Hammer if (tags == null || tags.isEmpty()) { 258060f3699SOliver Lockwood return Optional.empty(); 2596a4c77e6SCarsten Hammer } else if (matchers.isEmpty()) { 260f9de9175SHåvard Wall Collections.sort(tags, TAG_TIE_BREAKER); 261060f3699SOliver Lockwood return Optional.of(tags.get(0)); 262af0867cbSOliver Lockwood } else { 263df626364SMatthias Sohn // Find the first tag that matches in the stream of all tags 264df626364SMatthias Sohn // filtered by matchers ordered by tie break order 265df626364SMatthias Sohn Stream<Ref> matchingTags = Stream.empty(); 26610ab407fSThomas Wolf for (FileNameMatcher matcher : matchers) { 267df626364SMatthias Sohn Stream<Ref> m = tags.stream().filter( 26810ab407fSThomas Wolf tag -> { 269*276fcb2aSJason Yeo matcher.append(formatRefName(tag.getName())); 27010ab407fSThomas Wolf boolean result = matcher.isMatch(); 27110ab407fSThomas Wolf matcher.reset(); 27210ab407fSThomas Wolf return result; 27310ab407fSThomas Wolf }); 274df626364SMatthias Sohn matchingTags = Stream.of(matchingTags, m).flatMap(i -> i); 275af0867cbSOliver Lockwood } 276df626364SMatthias Sohn return matchingTags.sorted(TAG_TIE_BREAKER).findFirst(); 277060f3699SOliver Lockwood } 278060f3699SOliver Lockwood } 279060f3699SOliver Lockwood getObjectIdFromRef(Ref r)28051599ebbSDavid Pursehouse private ObjectId getObjectIdFromRef(Ref r) throws JGitInternalException { 28151599ebbSDavid Pursehouse try { 28251599ebbSDavid Pursehouse ObjectId key = repo.getRefDatabase().peel(r).getPeeledObjectId(); 283060f3699SOliver Lockwood if (key == null) { 284060f3699SOliver Lockwood key = r.getObjectId(); 285060f3699SOliver Lockwood } 286060f3699SOliver Lockwood return key; 28751599ebbSDavid Pursehouse } catch (IOException e) { 28851599ebbSDavid Pursehouse throw new JGitInternalException(e.getMessage(), e); 28951599ebbSDavid Pursehouse } 290060f3699SOliver Lockwood } 291af0867cbSOliver Lockwood 292af0867cbSOliver Lockwood /** 2930210e0e2SMatthias Sohn * {@inheritDoc} 2940210e0e2SMatthias Sohn * <p> 295b15c617dSMatthias Sohn * Describes the specified commit. Target defaults to HEAD if no commit was 296b15c617dSMatthias Sohn * set explicitly. 297f045a68aSKohsuke Kawaguchi */ 298f045a68aSKohsuke Kawaguchi @Override call()299f045a68aSKohsuke Kawaguchi public String call() throws GitAPIException { 300f045a68aSKohsuke Kawaguchi try { 301f045a68aSKohsuke Kawaguchi checkCallable(); 3025429d1a0SMarcel Trautwein if (target == null) { 303b15c617dSMatthias Sohn setTarget(Constants.HEAD); 3045429d1a0SMarcel Trautwein } 305f045a68aSKohsuke Kawaguchi 3069edf9bf2SMatthias Sohn Collection<Ref> tagList = repo.getRefDatabase() 307*276fcb2aSJason Yeo .getRefsByPrefix(useAll ? R_REFS : R_TAGS); 308060f3699SOliver Lockwood Map<ObjectId, List<Ref>> tags = tagList.stream() 3095429d1a0SMarcel Trautwein .filter(this::filterLightweightTags) 310060f3699SOliver Lockwood .collect(Collectors.groupingBy(this::getObjectIdFromRef)); 311f045a68aSKohsuke Kawaguchi 312f045a68aSKohsuke Kawaguchi // combined flags of all the candidate instances 313f045a68aSKohsuke Kawaguchi final RevFlagSet allFlags = new RevFlagSet(); 314f045a68aSKohsuke Kawaguchi 315f045a68aSKohsuke Kawaguchi /** 316f045a68aSKohsuke Kawaguchi * Tracks the depth of each tag as we find them. 317f045a68aSKohsuke Kawaguchi */ 318f045a68aSKohsuke Kawaguchi class Candidate { 319f045a68aSKohsuke Kawaguchi final Ref tag; 320f045a68aSKohsuke Kawaguchi final RevFlag flag; 321f045a68aSKohsuke Kawaguchi 322f045a68aSKohsuke Kawaguchi /** 323f045a68aSKohsuke Kawaguchi * This field counts number of commits that are reachable from 324f045a68aSKohsuke Kawaguchi * the tip but not reachable from the tag. 325f045a68aSKohsuke Kawaguchi */ 326f045a68aSKohsuke Kawaguchi int depth; 327f045a68aSKohsuke Kawaguchi 328f045a68aSKohsuke Kawaguchi Candidate(RevCommit commit, Ref tag) { 329f045a68aSKohsuke Kawaguchi this.tag = tag; 330f045a68aSKohsuke Kawaguchi this.flag = w.newFlag(tag.getName()); 331f045a68aSKohsuke Kawaguchi // we'll mark all the nodes reachable from this tag accordingly 332f045a68aSKohsuke Kawaguchi allFlags.add(flag); 333f045a68aSKohsuke Kawaguchi w.carry(flag); 334f045a68aSKohsuke Kawaguchi commit.add(flag); 335f045a68aSKohsuke Kawaguchi // As of this writing, JGit carries a flag from a child to its parents 336f045a68aSKohsuke Kawaguchi // right before RevWalk.next() returns, so all the flags that are added 337f045a68aSKohsuke Kawaguchi // must be manually carried to its parents. If that gets fixed, 338f045a68aSKohsuke Kawaguchi // this will be unnecessary. 339f045a68aSKohsuke Kawaguchi commit.carry(flag); 340f045a68aSKohsuke Kawaguchi } 341f045a68aSKohsuke Kawaguchi 342f045a68aSKohsuke Kawaguchi /** 343f045a68aSKohsuke Kawaguchi * Does this tag contain the given commit? 344f045a68aSKohsuke Kawaguchi */ 345f045a68aSKohsuke Kawaguchi boolean reaches(RevCommit c) { 346f045a68aSKohsuke Kawaguchi return c.has(flag); 347f045a68aSKohsuke Kawaguchi } 348f045a68aSKohsuke Kawaguchi 349f045a68aSKohsuke Kawaguchi String describe(ObjectId tip) throws IOException { 3500e3ddea1SChristian Halstrick return longDescription(tag, depth, tip); 351f045a68aSKohsuke Kawaguchi } 3520e3ddea1SChristian Halstrick 353f045a68aSKohsuke Kawaguchi } 3543b444863SDavid Pursehouse List<Candidate> candidates = new ArrayList<>(); // all the candidates we find 355f045a68aSKohsuke Kawaguchi 356060f3699SOliver Lockwood // is the target already pointing to a suitable tag? if so, we are done! 357060f3699SOliver Lockwood Optional<Ref> bestMatch = getBestMatch(tags.get(target)); 358060f3699SOliver Lockwood if (bestMatch.isPresent()) { 359060f3699SOliver Lockwood return longDesc ? longDescription(bestMatch.get(), 0, target) : 360*276fcb2aSJason Yeo formatRefName(bestMatch.get().getName()); 3610e3ddea1SChristian Halstrick } 362f045a68aSKohsuke Kawaguchi 363f045a68aSKohsuke Kawaguchi w.markStart(target); 364f045a68aSKohsuke Kawaguchi 365f045a68aSKohsuke Kawaguchi int seen = 0; // commit seen thus far 366f045a68aSKohsuke Kawaguchi RevCommit c; 367f045a68aSKohsuke Kawaguchi while ((c = w.next()) != null) { 368f045a68aSKohsuke Kawaguchi if (!c.hasAny(allFlags)) { 369f045a68aSKohsuke Kawaguchi // if a tag already dominates this commit, 370f045a68aSKohsuke Kawaguchi // then there's no point in picking a tag on this commit 371f045a68aSKohsuke Kawaguchi // since the one that dominates it is always more preferable 372060f3699SOliver Lockwood bestMatch = getBestMatch(tags.get(c)); 373060f3699SOliver Lockwood if (bestMatch.isPresent()) { 374060f3699SOliver Lockwood Candidate cd = new Candidate(c, bestMatch.get()); 375f045a68aSKohsuke Kawaguchi candidates.add(cd); 376f045a68aSKohsuke Kawaguchi cd.depth = seen; 377f045a68aSKohsuke Kawaguchi } 378f045a68aSKohsuke Kawaguchi } 379f045a68aSKohsuke Kawaguchi 380f045a68aSKohsuke Kawaguchi // if the newly discovered commit isn't reachable from a tag that we've seen 381f045a68aSKohsuke Kawaguchi // it counts toward the total depth. 382f045a68aSKohsuke Kawaguchi for (Candidate cd : candidates) { 383f045a68aSKohsuke Kawaguchi if (!cd.reaches(c)) 384f045a68aSKohsuke Kawaguchi cd.depth++; 385f045a68aSKohsuke Kawaguchi } 386f045a68aSKohsuke Kawaguchi 387f045a68aSKohsuke Kawaguchi // if we have search going for enough tags, we will start 388f045a68aSKohsuke Kawaguchi // closing down. JGit can only give us a finite number of bits, 389f045a68aSKohsuke Kawaguchi // so we can't track all tags even if we wanted to. 390f045a68aSKohsuke Kawaguchi if (candidates.size() >= maxCandidates) 391f045a68aSKohsuke Kawaguchi break; 392f045a68aSKohsuke Kawaguchi 393f045a68aSKohsuke Kawaguchi // TODO: if all the commits in the queue of RevWalk has allFlags 394f045a68aSKohsuke Kawaguchi // there's no point in continuing search as we'll not discover any more 395f045a68aSKohsuke Kawaguchi // tags. But RevWalk doesn't expose this. 396f045a68aSKohsuke Kawaguchi seen++; 397f045a68aSKohsuke Kawaguchi } 398f045a68aSKohsuke Kawaguchi 399f045a68aSKohsuke Kawaguchi // at this point we aren't adding any more tags to our search, 400f045a68aSKohsuke Kawaguchi // but we still need to count all the depths correctly. 401f045a68aSKohsuke Kawaguchi while ((c = w.next()) != null) { 402f045a68aSKohsuke Kawaguchi if (c.hasAll(allFlags)) { 403f045a68aSKohsuke Kawaguchi // no point in visiting further from here, so cut the search here 404f045a68aSKohsuke Kawaguchi for (RevCommit p : c.getParents()) 405f045a68aSKohsuke Kawaguchi p.add(RevFlag.SEEN); 406f045a68aSKohsuke Kawaguchi } else { 407f045a68aSKohsuke Kawaguchi for (Candidate cd : candidates) { 408f045a68aSKohsuke Kawaguchi if (!cd.reaches(c)) 409f045a68aSKohsuke Kawaguchi cd.depth++; 410f045a68aSKohsuke Kawaguchi } 411f045a68aSKohsuke Kawaguchi } 412f045a68aSKohsuke Kawaguchi } 413f045a68aSKohsuke Kawaguchi 414f045a68aSKohsuke Kawaguchi // if all the nodes are dominated by all the tags, the walk stops 415237c76fdSSebastian Schuberth if (candidates.isEmpty()) { 416237c76fdSSebastian Schuberth return always ? w.getObjectReader().abbreviate(target).name() : null; 417237c76fdSSebastian Schuberth } 418f045a68aSKohsuke Kawaguchi 41984fc5c90SCarsten Hammer Candidate best = Collections.min(candidates, 42084fc5c90SCarsten Hammer (Candidate o1, Candidate o2) -> o1.depth - o2.depth); 421f045a68aSKohsuke Kawaguchi 422f045a68aSKohsuke Kawaguchi return best.describe(target); 423f045a68aSKohsuke Kawaguchi } catch (IOException e) { 424f045a68aSKohsuke Kawaguchi throw new JGitInternalException(e.getMessage(), e); 425f045a68aSKohsuke Kawaguchi } finally { 426f045a68aSKohsuke Kawaguchi setCallable(false); 4275f8308bdSMatthias Sohn w.close(); 428f045a68aSKohsuke Kawaguchi } 429f045a68aSKohsuke Kawaguchi } 4305429d1a0SMarcel Trautwein 4315429d1a0SMarcel Trautwein /** 432*276fcb2aSJason Yeo * Removes the refs/ or refs/tags prefix from tag names 433*276fcb2aSJason Yeo * @param name the name of the tag 434*276fcb2aSJason Yeo * @return the tag name with its prefix removed 435*276fcb2aSJason Yeo */ formatRefName(String name)436*276fcb2aSJason Yeo private String formatRefName(String name) { 437*276fcb2aSJason Yeo return name.startsWith(R_TAGS) ? name.substring(R_TAGS.length()) : 438*276fcb2aSJason Yeo name.substring(R_REFS.length()); 439*276fcb2aSJason Yeo } 440*276fcb2aSJason Yeo 441*276fcb2aSJason Yeo /** 4425429d1a0SMarcel Trautwein * Whether we use lightweight tags or not for describe Candidates 4435429d1a0SMarcel Trautwein * 4445429d1a0SMarcel Trautwein * @param ref 4455429d1a0SMarcel Trautwein * reference under inspection 4465429d1a0SMarcel Trautwein * @return true if it should be used for describe or not regarding 4475429d1a0SMarcel Trautwein * {@link org.eclipse.jgit.api.DescribeCommand#useTags} 4485429d1a0SMarcel Trautwein */ 4495429d1a0SMarcel Trautwein @SuppressWarnings("null") filterLightweightTags(Ref ref)4505429d1a0SMarcel Trautwein private boolean filterLightweightTags(Ref ref) { 4515429d1a0SMarcel Trautwein ObjectId id = ref.getObjectId(); 4525429d1a0SMarcel Trautwein try { 453*276fcb2aSJason Yeo return this.useAll || this.useTags || (id != null && (w.parseTag(id) != null)); 4545429d1a0SMarcel Trautwein } catch (IOException e) { 4555429d1a0SMarcel Trautwein return false; 4565429d1a0SMarcel Trautwein } 4575429d1a0SMarcel Trautwein } 458f045a68aSKohsuke Kawaguchi } 459