This enables jgit to use any refs in the refs/ namespace when describing commits. Signed-off-by: Jason Yeo <jasonyeo88@gmail.com> Change-Id: I1fa22d1c39c0e2f5e4c2938c9751d8556494ac26tags/v5.10.0.202011041322-m2
usage_StopTrackingAFile=Stop tracking a file | usage_StopTrackingAFile=Stop tracking a file | ||||
usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions | usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions | ||||
usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs | usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs | ||||
usage_UseAll=Use all refs found in refs/ | |||||
usage_UseTags=Use any tag including lightweight tags | usage_UseTags=Use any tag including lightweight tags | ||||
usage_WriteDirCache=Write the DirCache | usage_WriteDirCache=Write the DirCache | ||||
usage_abbrevCommits=abbreviate commits to N + 1 digits | usage_abbrevCommits=abbreviate commits to N + 1 digits |
@Option(name = "--long", usage = "usage_LongFormat") | @Option(name = "--long", usage = "usage_LongFormat") | ||||
private boolean longDesc; | private boolean longDesc; | ||||
@Option(name = "--all", usage = "usage_UseTags") | |||||
private boolean useAll; | |||||
@Option(name = "--tags", usage = "usage_UseTags") | @Option(name = "--tags", usage = "usage_UseTags") | ||||
private boolean useTags; | private boolean useTags; | ||||
cmd.setTarget(tree); | cmd.setTarget(tree); | ||||
} | } | ||||
cmd.setLong(longDesc); | cmd.setLong(longDesc); | ||||
cmd.setAll(useAll); | |||||
cmd.setTags(useTags); | cmd.setTags(useTags); | ||||
cmd.setAlways(always); | cmd.setAlways(always); | ||||
cmd.setMatch(patterns.toArray(new String[0])); | cmd.setMatch(patterns.toArray(new String[0])); |
assertEquals( | assertEquals( | ||||
"2 commits for describe commit increment expected since lightweight tag: c4 and c3", | "2 commits for describe commit increment expected since lightweight tag: c4 and c3", | ||||
"t2-2-g119892b", describe(c4)); // 2 commits: c4 and c3 | "t2-2-g119892b", describe(c4)); // 2 commits: c4 and c3 | ||||
} else if (!useAnnotatedTags && !describeUseAllTags) { | |||||
} else if (!useAnnotatedTags) { | |||||
assertEquals("no matching commits expected", null, describe(c4)); | assertEquals("no matching commits expected", null, describe(c4)); | ||||
} else { | } else { | ||||
assertEquals( | assertEquals( | ||||
} | } | ||||
} | } | ||||
@Test | |||||
public void testDescribeUseAllRefsMaster() throws Exception { | |||||
final ObjectId c1 = modify("aaa"); | |||||
tag("t1"); | |||||
if (useAnnotatedTags || describeUseAllTags) { | |||||
assertEquals("t1", describe(c1)); | |||||
} else { | |||||
assertEquals(null, describe(c1)); | |||||
} | |||||
assertEquals("heads/master", describeAll(c1)); | |||||
} | |||||
/** | |||||
* Branch off from master and then tag | |||||
* | |||||
* <pre> | |||||
* c1 -+ -> c2 | |||||
* | | |||||
* +-> t1 | |||||
* </pre> | |||||
* @throws Exception | |||||
* */ | |||||
@Test | |||||
public void testDescribeUseAllRefsBranch() throws Exception { | |||||
final ObjectId c1 = modify("aaa"); | |||||
modify("bbb"); | |||||
branch("b", c1); | |||||
final ObjectId c3 = modify("ccc"); | |||||
tag("t1"); | |||||
if (!useAnnotatedTags && !describeUseAllTags) { | |||||
assertEquals(null, describe(c3)); | |||||
} else { | |||||
assertEquals("t1", describe(c3)); | |||||
} | |||||
assertEquals("heads/b", describeAll(c3)); | |||||
} | |||||
private ObjectId merge(ObjectId c2) throws GitAPIException { | private ObjectId merge(ObjectId c2) throws GitAPIException { | ||||
return git.merge().include(c2).call().getNewHead(); | return git.merge().include(c2).call().getNewHead(); | ||||
} | } | ||||
return describe(c1, false, false); | return describe(c1, false, false); | ||||
} | } | ||||
private String describeAll(ObjectId c1) throws GitAPIException, IOException { | |||||
return git.describe().setTarget(c1).setTags(describeUseAllTags) | |||||
.setLong(false).setAlways(false).setAll(true).call(); | |||||
} | |||||
private String describe(ObjectId c1, String... patterns) throws Exception { | private String describe(ObjectId c1, String... patterns) throws Exception { | ||||
return git.describe().setTarget(c1).setTags(describeUseAllTags) | return git.describe().setTarget(c1).setTags(describeUseAllTags) | ||||
.setMatch(patterns).call(); | .setMatch(patterns).call(); |
*/ | */ | ||||
package org.eclipse.jgit.api; | package org.eclipse.jgit.api; | ||||
import static org.eclipse.jgit.lib.Constants.R_REFS; | |||||
import static org.eclipse.jgit.lib.Constants.R_TAGS; | import static org.eclipse.jgit.lib.Constants.R_TAGS; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
*/ | */ | ||||
private List<FileNameMatcher> matchers = new ArrayList<>(); | private List<FileNameMatcher> matchers = new ArrayList<>(); | ||||
/** | |||||
* Whether to use all refs in the refs/ namespace | |||||
*/ | |||||
private boolean useAll; | |||||
/** | /** | ||||
* Whether to use all tags (incl. lightweight) or not. | * Whether to use all tags (incl. lightweight) or not. | ||||
*/ | */ | ||||
return this; | return this; | ||||
} | } | ||||
/** | |||||
* Instead of using only the annotated tags, use any ref found in refs/ | |||||
* namespace. This option enables matching any known branch, | |||||
* remote-tracking branch, or lightweight tag. | |||||
* | |||||
* @param all | |||||
* <code>true</code> enables matching any ref found in refs/ | |||||
* like setting option --all in c git | |||||
* @return {@code this} | |||||
* @since 5.10 | |||||
*/ | |||||
public DescribeCommand setAll(boolean all) { | |||||
this.useAll = all; | |||||
return this; | |||||
} | |||||
/** | /** | ||||
* Instead of using only the annotated tags, use any tag found in refs/tags | * Instead of using only the annotated tags, use any tag found in refs/tags | ||||
* namespace. This option enables matching lightweight (non-annotated) tags | * namespace. This option enables matching lightweight (non-annotated) tags | ||||
private String longDescription(Ref tag, int depth, ObjectId tip) | private String longDescription(Ref tag, int depth, ObjectId tip) | ||||
throws IOException { | throws IOException { | ||||
return String.format( | return String.format( | ||||
"%s-%d-g%s", tag.getName().substring(R_TAGS.length()), //$NON-NLS-1$ | |||||
"%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$ | |||||
Integer.valueOf(depth), w.getObjectReader().abbreviate(tip) | Integer.valueOf(depth), w.getObjectReader().abbreviate(tip) | ||||
.name()); | .name()); | ||||
} | } | ||||
for (FileNameMatcher matcher : matchers) { | for (FileNameMatcher matcher : matchers) { | ||||
Stream<Ref> m = tags.stream().filter( | Stream<Ref> m = tags.stream().filter( | ||||
tag -> { | tag -> { | ||||
matcher.append( | |||||
tag.getName().substring(R_TAGS.length())); | |||||
matcher.append(formatRefName(tag.getName())); | |||||
boolean result = matcher.isMatch(); | boolean result = matcher.isMatch(); | ||||
matcher.reset(); | matcher.reset(); | ||||
return result; | return result; | ||||
} | } | ||||
Collection<Ref> tagList = repo.getRefDatabase() | Collection<Ref> tagList = repo.getRefDatabase() | ||||
.getRefsByPrefix(R_TAGS); | |||||
.getRefsByPrefix(useAll ? R_REFS : R_TAGS); | |||||
Map<ObjectId, List<Ref>> tags = tagList.stream() | Map<ObjectId, List<Ref>> tags = tagList.stream() | ||||
.filter(this::filterLightweightTags) | .filter(this::filterLightweightTags) | ||||
.collect(Collectors.groupingBy(this::getObjectIdFromRef)); | .collect(Collectors.groupingBy(this::getObjectIdFromRef)); | ||||
Optional<Ref> bestMatch = getBestMatch(tags.get(target)); | Optional<Ref> bestMatch = getBestMatch(tags.get(target)); | ||||
if (bestMatch.isPresent()) { | if (bestMatch.isPresent()) { | ||||
return longDesc ? longDescription(bestMatch.get(), 0, target) : | return longDesc ? longDescription(bestMatch.get(), 0, target) : | ||||
bestMatch.get().getName().substring(R_TAGS.length()); | |||||
formatRefName(bestMatch.get().getName()); | |||||
} | } | ||||
w.markStart(target); | w.markStart(target); | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Removes the refs/ or refs/tags prefix from tag names | |||||
* @param name the name of the tag | |||||
* @return the tag name with its prefix removed | |||||
*/ | |||||
private String formatRefName(String name) { | |||||
return name.startsWith(R_TAGS) ? name.substring(R_TAGS.length()) : | |||||
name.substring(R_REFS.length()); | |||||
} | |||||
/** | /** | ||||
* Whether we use lightweight tags or not for describe Candidates | * Whether we use lightweight tags or not for describe Candidates | ||||
* | * | ||||
private boolean filterLightweightTags(Ref ref) { | private boolean filterLightweightTags(Ref ref) { | ||||
ObjectId id = ref.getObjectId(); | ObjectId id = ref.getObjectId(); | ||||
try { | try { | ||||
return this.useTags || (id != null && (w.parseTag(id) != null)); | |||||
return this.useAll || this.useTags || (id != null && (w.parseTag(id) != null)); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
return false; | return false; | ||||
} | } |