Browse Source

Implement git describe --all

This enables jgit to use any refs in the refs/ namespace when describing
commits.

Signed-off-by: Jason Yeo <jasonyeo88@gmail.com>
Change-Id: I1fa22d1c39c0e2f5e4c2938c9751d8556494ac26
tags/v5.10.0.202011041322-m2
Jason Yeo 3 years ago
parent
commit
276fcb2a11

+ 1
- 0
org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties View File

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

+ 4
- 0
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java View File

@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]));

+ 46
- 1
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java View File

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();

+ 37
- 6
org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java View File

*/ */
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;
} }

Loading…
Cancel
Save