diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/treewalk')
19 files changed, 355 insertions, 145 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java index 3ef5b29a55..65b18086b4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java @@ -718,7 +718,6 @@ public abstract class AbstractTreeIterator { System.arraycopy(path, pathOffset, buffer, offset, pathLen - pathOffset); } - /** {@inheritDoc} */ @SuppressWarnings("nls") @Override public String toString() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java index 5c3f6aefe1..c6d50d35f1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java @@ -191,7 +191,6 @@ public class CanonicalTreeParser extends AbstractTreeIterator { reset(reader.open(id, OBJ_TREE).getCachedBytes()); } - /** {@inheritDoc} */ @Override public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader, final MutableObjectId idBuffer) @@ -227,51 +226,43 @@ public class CanonicalTreeParser extends AbstractTreeIterator { return p; } - /** {@inheritDoc} */ @Override public CanonicalTreeParser createSubtreeIterator(ObjectReader reader) throws IncorrectObjectTypeException, IOException { return createSubtreeIterator(reader, new MutableObjectId()); } - /** {@inheritDoc} */ @Override public boolean hasId() { return true; } - /** {@inheritDoc} */ @Override public byte[] idBuffer() { return raw; } - /** {@inheritDoc} */ @Override public int idOffset() { return nextPtr - OBJECT_ID_LENGTH; } - /** {@inheritDoc} */ @Override public void reset() { if (!first()) reset(raw); } - /** {@inheritDoc} */ @Override public boolean first() { return currPtr == 0; } - /** {@inheritDoc} */ @Override public boolean eof() { return currPtr == raw.length; } - /** {@inheritDoc} */ @Override public void next(int delta) { if (delta == 1) { @@ -301,7 +292,6 @@ public class CanonicalTreeParser extends AbstractTreeIterator { parseEntry(); } - /** {@inheritDoc} */ @Override public void back(int delta) { if (delta == 1 && 0 <= prevPtr) { @@ -376,6 +366,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { * @return {@link org.eclipse.jgit.attributes.AttributesNode} for the * current entry. * @throws java.io.IOException + * if an IO error occurred * @since 4.2 */ public AttributesNode getEntryAttributesNode(ObjectReader reader) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java index 0661c9044a..32368dcfb8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java @@ -57,75 +57,63 @@ public class EmptyTreeIterator extends AbstractTreeIterator { pathLen = childPathOffset - 1; } - /** {@inheritDoc} */ @Override public AbstractTreeIterator createSubtreeIterator(ObjectReader reader) throws IncorrectObjectTypeException, IOException { return new EmptyTreeIterator(this); } - /** {@inheritDoc} */ @Override public boolean hasId() { return false; } - /** {@inheritDoc} */ @Override public ObjectId getEntryObjectId() { return ObjectId.zeroId(); } - /** {@inheritDoc} */ @Override public byte[] idBuffer() { return zeroid; } - /** {@inheritDoc} */ @Override public int idOffset() { return 0; } - /** {@inheritDoc} */ @Override public void reset() { // Do nothing. } - /** {@inheritDoc} */ @Override public boolean first() { return true; } - /** {@inheritDoc} */ @Override public boolean eof() { return true; } - /** {@inheritDoc} */ @Override public void next(int delta) throws CorruptObjectException { // Do nothing. } - /** {@inheritDoc} */ @Override public void back(int delta) throws CorruptObjectException { // Do nothing. } - /** {@inheritDoc} */ @Override public void stopWalk() { if (parent != null) parent.stopWalk(); } - /** {@inheritDoc} */ @Override protected boolean needsStopWalk() { return parent != null && parent.needsStopWalk(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index 60b92d7271..0cac374844 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -175,7 +175,6 @@ public class FileTreeIterator extends WorkingTreeIterator { init(entries()); } - /** {@inheritDoc} */ @Override public AbstractTreeIterator createSubtreeIterator(ObjectReader reader) throws IncorrectObjectTypeException, IOException { @@ -372,12 +371,6 @@ public class FileTreeIterator extends WorkingTreeIterator { return attributes.getLength(); } - @Override - @Deprecated - public long getLastModified() { - return attributes.getLastModifiedInstant().toEpochMilli(); - } - /** * @since 5.1.9 */ @@ -425,13 +418,11 @@ public class FileTreeIterator extends WorkingTreeIterator { return ((FileEntry) current()).getFile(); } - /** {@inheritDoc} */ @Override protected byte[] idSubmodule(Entry e) { return idSubmodule(getDirectory(), e); } - /** {@inheritDoc} */ @Override protected String readSymlinkTarget(Entry entry) throws IOException { return fs.readSymLink(getEntryFile()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index ece945232e..31c216b4a8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -38,12 +38,12 @@ import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; @@ -78,6 +78,8 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { private static final AbstractTreeIterator[] NO_TREES = {}; /** + * Type of operation to retrieve git attributes for. + * * @since 4.2 */ public enum OperationType { @@ -548,7 +550,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * <p> * Retrieve the git attributes for the current entry. * - * <h3>Git attribute computation</h3> + * <h4>Git attribute computation</h4> * * <ul> * <li>Get the attributes matching the current path entry from the info file @@ -563,11 +565,10 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * <li>In the end, completes the list of matching attributes using the * global attribute file define in the configuration (see * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li> - * * </ul> * * - * <h3>Iterator constraints</h3> + * <h4>Iterator constraints</h4> * * <p> * In order to have a correct list of attributes for the current entry, this @@ -960,6 +961,8 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * iterators to manage only one list of items, with the diving handled by * recursive trees. * + * @param <T> + * Type of returned {@code AbstractTreeIterator} * @param nth * tree to obtain the current iterator of. * @param clazz @@ -1376,12 +1379,14 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** - * Returns an AbstractTreeIterator from {@code trees} with the smallest name, and sets its - * {@code matches} field. This may clobber {@code matches} in other {@code tree}s. Other iterators - * at the same name will have their {@code matches} pointing to the same {@code min()} value. + * Returns an AbstractTreeIterator from {@code trees} with the smallest + * name, and sets its {@code matches} field. This may clobber + * {@code matches} in other {@code tree}s. Other iterators at the same name + * will have their {@code matches} pointing to the same {@code min()} value. * * @return the smallest tree iterator available. * @throws CorruptObjectException + * if an object is corrupt */ @SuppressWarnings("unused") AbstractTreeIterator min() throws CorruptObjectException { @@ -1488,6 +1493,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * {{@link #getSmudgeCommand(int)} instead. * @return a filter command * @throws java.io.IOException + * if an IO error occurred * @since 4.2 */ public String getFilterCommand(String filterCommandType) @@ -1510,7 +1516,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } return filterCommand.replaceAll("%f", //$NON-NLS-1$ Matcher.quoteReplacement( - QuotedString.BOURNE.quote((getPathString())))); + QuotedString.BOURNE.quote(getPathString()))); } /** @@ -1521,6 +1527,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * of the tree the item to be smudged is in * @return a filter command * @throws java.io.IOException + * if an IO error occurred * @since 6.1 */ public String getSmudgeCommand(int index) @@ -1536,6 +1543,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * to use * @return a filter command * @throws java.io.IOException + * if an IO error occurred * @since 6.1 */ public String getSmudgeCommand(Attributes attributes) throws IOException { @@ -1558,7 +1566,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } return filterCommand.replaceAll("%f", //$NON-NLS-1$ Matcher.quoteReplacement( - QuotedString.BOURNE.quote((getPathString())))); + QuotedString.BOURNE.quote(getPathString()))); } /** @@ -1579,10 +1587,16 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { */ private String getFilterCommandDefinition(String filterDriverName, String filterCommandType) { + if (config == null) { + return null; + } String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$ String filterCommand = filterCommandsByNameDotType.get(key); if (filterCommand != null) return filterCommand; + if (config == null) { + return null; + } filterCommand = config.getString(ConfigConstants.CONFIG_FILTER_SECTION, filterDriverName, filterCommandType); boolean useBuiltin = config.getBoolean( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index b5d6610d52..f16d800f63 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -15,7 +15,6 @@ package org.eclipse.jgit.treewalk; import static java.nio.charset.StandardCharsets.UTF_8; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -25,6 +24,7 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetEncoder; +import java.nio.file.Files; import java.nio.file.Path; import java.text.MessageFormat; import java.time.Instant; @@ -68,8 +68,10 @@ import org.eclipse.jgit.util.Holder; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.Paths; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.SystemReader; import org.eclipse.jgit.util.TemporaryBuffer; import org.eclipse.jgit.util.TemporaryBuffer.LocalFile; +import org.eclipse.jgit.util.io.ByteBufferInputStream; import org.eclipse.jgit.util.io.EolStreamTypeUtil; import org.eclipse.jgit.util.sha1.SHA1; @@ -270,7 +272,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return state.walkIgnored; } - /** {@inheritDoc} */ @Override public boolean hasId() { if (contentIdFromPtr == ptr) @@ -278,7 +279,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return (mode & FileMode.TYPE_MASK) == FileMode.TYPE_FILE; } - /** {@inheritDoc} */ @Override public byte[] idBuffer() { if (contentIdFromPtr == ptr) @@ -316,7 +316,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return zeroid; } - /** {@inheritDoc} */ @Override public boolean isWorkTree() { return true; @@ -408,9 +407,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) { InputStream is = e.openInputStream(); try { - ByteBuffer rawbuf = IO.readWholeStream(is, (int) len); - rawbuf = filterClean(rawbuf.array(), rawbuf.limit()); - return rawbuf.limit(); + ByteBuffer filteredData = IO.readWholeStream(filterClean(is), + (int) len); + return filteredData.remaining(); } finally { safeClose(is); } @@ -439,10 +438,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } if (len <= MAXIMUM_FILE_SIZE_TO_READ_FULLY) { - ByteBuffer rawbuf = IO.readWholeStream(is, (int) len); - rawbuf = filterClean(rawbuf.array(), rawbuf.limit()); - canonLen = rawbuf.limit(); - return new ByteArrayInputStream(rawbuf.array(), 0, (int) canonLen); + ByteBuffer filteredData = IO.readWholeStream(filterClean(is), (int) len); + canonLen = filteredData.remaining(); + return new ByteBufferInputStream(filteredData); } if (getCleanFilterCommand() == null && isBinary(e)) { @@ -478,16 +476,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } - private ByteBuffer filterClean(byte[] src, int n) - throws IOException { - InputStream in = new ByteArrayInputStream(src); - try { - return IO.readWholeStream(filterClean(in), n); - } finally { - safeClose(in); - } - } - private InputStream filterClean(InputStream in) throws IOException { in = EolStreamTypeUtil.wrapInputStream(in, @@ -510,6 +498,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { filterProcessBuilder.directory(repository.getWorkTree()); filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, repository.getDirectory().getAbsolutePath()); + filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY, + repository.getCommonDirectory().getAbsolutePath()); ExecutionResult result; try { result = fs.execute(filterProcessBuilder, in); @@ -549,13 +539,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return repository; } - /** {@inheritDoc} */ @Override public int idOffset() { return contentIdOffset; } - /** {@inheritDoc} */ @Override public void reset() { if (!first()) { @@ -565,19 +553,16 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } - /** {@inheritDoc} */ @Override public boolean first() { return ptr == 0; } - /** {@inheritDoc} */ @Override public boolean eof() { return ptr == entryCnt; } - /** {@inheritDoc} */ @Override public void next(int delta) throws CorruptObjectException { ptr += delta; @@ -586,7 +571,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } - /** {@inheritDoc} */ @Override public void back(int delta) throws CorruptObjectException { ptr -= delta; @@ -620,6 +604,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * * @return size of the content, in bytes * @throws java.io.IOException + * if an IO error occurred */ public long getEntryContentLength() throws IOException { if (canonLen == -1) { @@ -637,18 +622,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** * Get the last modified time of this entry. * - * @return last modified time of this file, in milliseconds since the epoch - * (Jan 1, 1970 UTC). - * @deprecated use {@link #getEntryLastModifiedInstant()} instead - */ - @Deprecated - public long getEntryLastModified() { - return current().getLastModified(); - } - - /** - * Get the last modified time of this entry. - * * @return last modified time of this file * @since 5.1.9 */ @@ -772,6 +745,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * @return the {@link org.eclipse.jgit.attributes.AttributesNode} for the * current entry. * @throws IOException + * if an IO error occurred */ public AttributesNode getEntryAttributesNode() throws IOException { if (attributesNode instanceof PerDirectoryAttributesNode) @@ -964,6 +938,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * access to repository objects if necessary. Should not be null. * @return true if content is most likely different. * @throws java.io.IOException + * if an IO error occurred * @since 3.3 */ public boolean isModified(DirCacheEntry entry, boolean forceContentCheck, @@ -1070,6 +1045,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * @return <code>true</code> if the content doesn't match, * <code>false</code> if it matches * @throws IOException + * if an IO error occurred */ private boolean contentCheck(DirCacheEntry entry, ObjectReader reader) throws IOException { @@ -1243,21 +1219,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * needs to compute the value they should cache the reference within an * instance member instead. * - * @return time since the epoch (in ms) of the last change. - * @deprecated use {@link #getLastModifiedInstant()} instead - */ - @Deprecated - public abstract long getLastModified(); - - /** - * Get the last modified time of this entry. - * <p> - * <b>Note: Efficient implementation required.</b> - * <p> - * The implementation of this method must be efficient. If a subclass - * needs to compute the value they should cache the reference within an - * instance member instead. - * * @return time of the last change. * @since 5.1.9 */ @@ -1334,7 +1295,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_EXCLUDESFILE, fs, null, null); if (path != null) { - loadRulesFromFile(coreExclude, path.toFile()); + if (Files.exists(path)) { + loadRulesFromFile(coreExclude, path.toFile()); + } + } else { + loadRulesFromDefaultFile(coreExclude, fs); } if (coreExclude.getRules().isEmpty()) { coreExclude = parent; @@ -1342,9 +1307,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { IgnoreNode infoExclude = new IgnoreNodeWithParent( coreExclude); - File exclude = fs.resolve(repository.getDirectory(), + File exclude = fs.resolve(repository.getCommonDirectory(), Constants.INFO_EXCLUDE); - loadRulesFromFile(infoExclude, exclude); + if (fs.exists(exclude)) { + loadRulesFromFile(infoExclude, exclude); + } if (infoExclude.getRules().isEmpty()) { infoExclude = null; } @@ -1366,9 +1333,19 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { private static void loadRulesFromFile(IgnoreNode r, File exclude) throws FileNotFoundException, IOException { - if (FS.DETECTED.exists(exclude)) { - try (FileInputStream in = new FileInputStream(exclude)) { - r.parse(exclude.getAbsolutePath(), in); + try (FileInputStream in = new FileInputStream(exclude)) { + r.parse(exclude.getAbsolutePath(), in); + } + } + + private static void loadRulesFromDefaultFile(IgnoreNode r, + FS fileSystem) throws FileNotFoundException, IOException { + Path cfg = SystemReader.getInstance() + .getXdgConfigDirectory(fileSystem); + if (cfg != null) { + Path cfgPath = cfg.resolve("git").resolve("ignore"); //$NON-NLS-1$ //$NON-NLS-2$ + if (Files.exists(cfgPath)) { + loadRulesFromFile(r, cfgPath.toFile()); } } } @@ -1450,6 +1427,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * @return the clean filter command for the current entry or * <code>null</code> if no such command is defined * @throws java.io.IOException + * if an IO error occurred * @since 4.2 */ public String getCleanFilterCommand() throws IOException { @@ -1472,6 +1450,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * {@link org.eclipse.jgit.treewalk.TreeWalk} is not based on a * {@link org.eclipse.jgit.lib.Repository} then null is returned. * @throws java.io.IOException + * if an IO error occurred * @since 4.3 */ public EolStreamType getEolStreamType() throws IOException { @@ -1486,6 +1465,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * {@link TreeWalk} is not based on a {@link Repository} then null * is returned. * @throws IOException + * if an IO error occurred */ private EolStreamType getEolStreamType(OperationType opType) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java index c6804da039..b35dbebd17 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java @@ -12,11 +12,14 @@ package org.eclipse.jgit.treewalk.filter; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; /** @@ -100,6 +103,13 @@ public abstract class AndTreeFilter extends TreeFilter { } @Override + public boolean shouldTreeWalk(RevCommit c, RevWalk rw, + MutableBoolean cpfUsed) { + return a.shouldTreeWalk(c, rw, cpfUsed) + && b.shouldTreeWalk(c, rw, cpfUsed); + } + + @Override public int matchFilter(TreeWalk walker) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -174,6 +184,13 @@ public abstract class AndTreeFilter extends TreeFilter { } @Override + public boolean shouldTreeWalk(RevCommit c, RevWalk rw, + MutableBoolean cpfUsed) { + return Arrays.stream(subfilters) + .allMatch(t -> t.shouldTreeWalk(c, rw, cpfUsed)); + } + + @Override public TreeFilter clone() { final TreeFilter[] s = new TreeFilter[subfilters.length]; for (int i = 0; i < s.length; i++) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java index cafa926ffc..33db6ea661 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java @@ -13,6 +13,10 @@ package org.eclipse.jgit.treewalk.filter; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + import org.eclipse.jgit.util.RawParseUtils; /** @@ -39,6 +43,7 @@ class ByteArraySet { * Create an empty set. * * @param capacity + * initial capacity of the set */ ByteArraySet(int capacity) { initTable(1 << Integer.highestOneBit((capacity * 2) - 1)); @@ -136,13 +141,19 @@ class ByteArraySet { } /** + * Returns number of arrays in the set + * * @return number of arrays in the set */ int size() { return size; } - /** @return true if {@link #size()} is 0. */ + /** + * Returns true if {@link #size()} is 0 + * + * @return true if {@link #size()} is 0 + */ boolean isEmpty() { return size == 0; } @@ -180,7 +191,6 @@ class ByteArraySet { table = new byte[sz][]; } - /** {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -291,4 +301,8 @@ class ByteArraySet { return ret; } + Set<byte[]> toSet() { + return Arrays.stream(toArray()).collect(Collectors.toSet()); + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ChangedPathTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ChangedPathTreeFilter.java new file mode 100644 index 0000000000..a74b9b617f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ChangedPathTreeFilter.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2025, Google LLC 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.treewalk.filter; + +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.commitgraph.ChangedPathFilter; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.StringUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Filter tree entries that modified the contents of particular file paths. + * <p> + * Equivalent to AndTreeFilter(PathFilter, AnyDiffFilter). This filter uses + * {@link org.eclipse.jgit.internal.storage.commitgraph.ChangedPathFilter} + * (bloom filters) when available to discard commits without diffing their + * trees. + * + * @since 7.3 + */ +public class ChangedPathTreeFilter extends TreeFilter { + + private TreeFilter pathFilter; + + private List<String> paths; + + private List<byte[]> rawPaths; + + /** + * Create a TreeFilter for trees modifying one or more user supplied paths. + * <p> + * Path strings are relative to the root of the repository. If the user's + * input should be assumed relative to a subdirectory of the repository the + * caller must prepend the subdirectory's path prior to creating the filter. + * <p> + * Path strings use '/' to delimit directories on all platforms. + * <p> + * Paths may appear in any order within the collection. Sorting may be done + * internally when the group is constructed if doing so will improve path + * matching performance. + * + * @param paths + * the paths to test against. Must have at least one entry. + * @return a new filter for the list of paths supplied. + */ + public static ChangedPathTreeFilter create(String... paths) { + return new ChangedPathTreeFilter(paths); + } + + private ChangedPathTreeFilter(String... paths) { + List<String> filtered = Arrays.stream(paths) + .map(s -> StringUtils.trim(s, '/')) + .collect(Collectors.toList()); + + if (filtered.size() == 0) + throw new IllegalArgumentException( + JGitText.get().atLeastOnePathIsRequired); + + if (filtered.stream().anyMatch(s -> s.isEmpty() || s.isBlank())) { + throw new IllegalArgumentException( + JGitText.get().emptyPathNotPermitted); + } + + this.paths = filtered; + this.rawPaths = this.paths.stream().map(Constants::encode) + .collect(Collectors.toList()); + if (filtered.size() == 1) { + this.pathFilter = PathFilter.create(paths[0]); + } else { + this.pathFilter = OrTreeFilter.create(Arrays.stream(paths) + .map(PathFilter::create).collect(Collectors.toList())); + } + } + + @Override + public boolean shouldTreeWalk(RevCommit c, RevWalk rw, + MutableBoolean cpfUsed) { + ChangedPathFilter cpf = c.getChangedPathFilter(rw); + if (cpf == null) { + return true; + } + if (cpfUsed != null) { + cpfUsed.orValue(true); + } + // return true if at least one path might exist in cpf + return rawPaths.stream().anyMatch(cpf::maybeContains); + } + + @Override + public boolean include(TreeWalk walker) throws IOException { + return pathFilter.include(walker) && ANY_DIFF.include(walker); + } + + @Override + public boolean shouldBeRecursive() { + return pathFilter.shouldBeRecursive() || ANY_DIFF.shouldBeRecursive(); + } + + @Override + public ChangedPathTreeFilter clone() { + return this; + } + + /** + * Get the paths this filter matches. + * + * @return the paths this filter matches. + */ + public List<String> getPaths() { + return paths; + } + + @SuppressWarnings("nls") + @Override + public String toString() { + return "(CHANGED_PATH(" + pathFilter.toString() + ")" // + + " AND " // + + ANY_DIFF.toString() + ")"; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java index 4731f345bc..cfdc4dd358 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java @@ -10,8 +10,9 @@ package org.eclipse.jgit.treewalk.filter; import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -62,9 +63,9 @@ public class IndexDiffFilter extends TreeFilter { private final Set<String> ignoredPaths = new HashSet<>(); - private final LinkedList<String> untrackedParentFolders = new LinkedList<>(); + private final ArrayDeque<String> untrackedParentFolders = new ArrayDeque<>(); - private final LinkedList<String> untrackedFolders = new LinkedList<>(); + private final ArrayDeque<String> untrackedFolders = new ArrayDeque<>(); /** * Creates a new instance of this filter. Do not use an instance of this @@ -106,7 +107,6 @@ public class IndexDiffFilter extends TreeFilter { this.honorIgnores = honorIgnores; } - /** {@inheritDoc} */ @Override public boolean include(TreeWalk tw) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -234,7 +234,6 @@ public class IndexDiffFilter extends TreeFilter { return tw.getTree(workingTree, WorkingTreeIterator.class); } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { // We cannot compare subtrees in the working tree, so encourage @@ -242,13 +241,11 @@ public class IndexDiffFilter extends TreeFilter { return true; } - /** {@inheritDoc} */ @Override public TreeFilter clone() { return this; } - /** {@inheritDoc} */ @Override public String toString() { return "INDEX_DIFF_FILTER"; //$NON-NLS-1$ @@ -276,12 +273,14 @@ public class IndexDiffFilter extends TreeFilter { * empty list will be returned. */ public List<String> getUntrackedFolders() { - LinkedList<String> ret = new LinkedList<>(untrackedFolders); + ArrayList<String> ret = new ArrayList<>(untrackedFolders); if (!untrackedParentFolders.isEmpty()) { String toBeAdded = untrackedParentFolders.getLast(); - while (!ret.isEmpty() && ret.getLast().startsWith(toBeAdded)) - ret.removeLast(); - ret.addLast(toBeAdded); + while (!ret.isEmpty() + && ret.get(ret.size() - 1).startsWith(toBeAdded)) { + ret.remove(ret.size() - 1); + } + ret.add(toBeAdded); } return ret; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilter.java index 163dc71daa..0c0b09e7bf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilter.java @@ -28,7 +28,6 @@ public final class InterIndexDiffFilter extends TreeFilter { */ public static final TreeFilter INSTANCE = new InterIndexDiffFilter(); - /** {@inheritDoc} */ @Override public boolean include(TreeWalk walker) { final int n = walker.getTreeCount(); @@ -57,19 +56,16 @@ public final class InterIndexDiffFilter extends TreeFilter { return false; } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { return false; } - /** {@inheritDoc} */ @Override public TreeFilter clone() { return this; } - /** {@inheritDoc} */ @Override public String toString() { return "INTERINDEX_DIFF"; //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotIgnoredFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotIgnoredFilter.java index 7d04f27f3c..25947da08f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotIgnoredFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotIgnoredFilter.java @@ -33,7 +33,6 @@ public class NotIgnoredFilter extends TreeFilter { this.index = workdirTreeIndex; } - /** {@inheritDoc} */ @Override public boolean include(TreeWalk tw) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -41,20 +40,17 @@ public class NotIgnoredFilter extends TreeFilter { return i == null || !i.isEntryIgnored(); } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { return false; } - /** {@inheritDoc} */ @Override public TreeFilter clone() { // immutable return this; } - /** {@inheritDoc} */ @SuppressWarnings("nls") @Override public String toString() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java index 4fb615321f..e9cd83c2e2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java @@ -38,13 +38,11 @@ public class NotTreeFilter extends TreeFilter { a = one; } - /** {@inheritDoc} */ @Override public TreeFilter negate() { return a; } - /** {@inheritDoc} */ @Override public boolean include(TreeWalk walker) throws MissingObjectException, IncorrectObjectTypeException, @@ -52,7 +50,6 @@ public class NotTreeFilter extends TreeFilter { return matchFilter(walker) == 0; } - /** {@inheritDoc} */ @Override public int matchFilter(TreeWalk walker) throws MissingObjectException, IncorrectObjectTypeException, @@ -69,20 +66,17 @@ public class NotTreeFilter extends TreeFilter { return -1; } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { return a.shouldBeRecursive(); } - /** {@inheritDoc} */ @Override public TreeFilter clone() { final TreeFilter n = a.clone(); return n == a ? this : new NotTreeFilter(n); } - /** {@inheritDoc} */ @Override public String toString() { return "NOT " + a.toString(); //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java index 3c18a9f98d..ce2382552b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java @@ -12,11 +12,14 @@ package org.eclipse.jgit.treewalk.filter; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; /** @@ -116,6 +119,13 @@ public abstract class OrTreeFilter extends TreeFilter { } @Override + public boolean shouldTreeWalk(RevCommit c, RevWalk rw, + MutableBoolean cpfUsed) { + return a.shouldTreeWalk(c, rw, cpfUsed) + || b.shouldTreeWalk(c, rw, cpfUsed); + } + + @Override public boolean shouldBeRecursive() { return a.shouldBeRecursive() || b.shouldBeRecursive(); } @@ -164,6 +174,13 @@ public abstract class OrTreeFilter extends TreeFilter { } @Override + public boolean shouldTreeWalk(RevCommit c, RevWalk rw, + MutableBoolean cpfUsed) { + return Arrays.stream(subfilters) + .anyMatch(t -> t.shouldTreeWalk(c, rw, cpfUsed)); + } + + @Override public boolean shouldBeRecursive() { for (TreeFilter f : subfilters) if (f.shouldBeRecursive()) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java index c94215fcb1..62422817ac 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java @@ -11,6 +11,10 @@ package org.eclipse.jgit.treewalk.filter; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.treewalk.TreeWalk; @@ -45,7 +49,8 @@ public class PathFilter extends TreeFilter { while (path.endsWith("/")) //$NON-NLS-1$ path = path.substring(0, path.length() - 1); if (path.length() == 0) - throw new IllegalArgumentException(JGitText.get().emptyPathNotPermitted); + throw new IllegalArgumentException( + JGitText.get().emptyPathNotPermitted); return new PathFilter(path); } @@ -67,19 +72,16 @@ public class PathFilter extends TreeFilter { return pathStr; } - /** {@inheritDoc} */ @Override public boolean include(TreeWalk walker) { return matchFilter(walker) <= 0; } - /** {@inheritDoc} */ @Override public int matchFilter(TreeWalk walker) { return walker.isPathMatch(pathRaw, pathRaw.length); } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { for (byte b : pathRaw) @@ -88,13 +90,18 @@ public class PathFilter extends TreeFilter { return false; } + @Override + public Optional<Set<byte[]>> getPathsBestEffort() { + Set<byte[]> s = Collections.singleton(pathRaw); + return Optional.of(s); + } + /** {@inheritDoc} */ @Override public PathFilter clone() { return this; } - /** {@inheritDoc} */ @Override @SuppressWarnings("nls") public String toString() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java index 59855572f2..4c0604ad56 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java @@ -12,6 +12,8 @@ package org.eclipse.jgit.treewalk.filter; import java.util.Collection; +import java.util.Optional; +import java.util.Set; import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.internal.JGitText; @@ -232,6 +234,15 @@ public class PathFilterGroup { } @Override + public Optional<Set<byte[]>> getPathsBestEffort() { + Set<byte[]> result = fullpaths.toSet(); + if (result.isEmpty()) { + return Optional.empty(); + } + return Optional.of(result); + } + + @Override public TreeFilter clone() { return this; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java index 3816d5ed00..ec25903211 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java @@ -56,13 +56,11 @@ public class PathSuffixFilter extends TreeFilter { pathRaw = Constants.encode(pathStr); } - /** {@inheritDoc} */ @Override public TreeFilter clone() { return this; } - /** {@inheritDoc} */ @Override public boolean include(TreeWalk walker) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -82,7 +80,6 @@ public class PathSuffixFilter extends TreeFilter { return super.matchFilter(walker); } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { return true; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/SkipWorkTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/SkipWorkTreeFilter.java index 1ed2ef32cd..e311523033 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/SkipWorkTreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/SkipWorkTreeFilter.java @@ -36,7 +36,6 @@ public class SkipWorkTreeFilter extends TreeFilter { this.treeIdx = treeIdx; } - /** {@inheritDoc} */ @Override public boolean include(TreeWalk walker) { DirCacheIterator i = walker.getTree(treeIdx, DirCacheIterator.class); @@ -47,19 +46,16 @@ public class SkipWorkTreeFilter extends TreeFilter { return e == null || !e.isSkipWorkTree(); } - /** {@inheritDoc} */ @Override public boolean shouldBeRecursive() { return false; } - /** {@inheritDoc} */ @Override public TreeFilter clone() { return this; } - /** {@inheritDoc} */ @SuppressWarnings("nls") @Override public String toString() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java index 6dbd508e48..8159843312 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java @@ -11,10 +11,15 @@ package org.eclipse.jgit.treewalk.filter; import java.io.IOException; +import java.util.Optional; +import java.util.Set; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.WorkingTreeIterator; @@ -188,10 +193,8 @@ public abstract class TreeFilter { * as thrown by {@link #include(TreeWalk)} * @since 4.7 */ - public int matchFilter(TreeWalk walker) - throws MissingObjectException, IncorrectObjectTypeException, - IOException - { + public int matchFilter(TreeWalk walker) throws MissingObjectException, + IncorrectObjectTypeException, IOException { return include(walker) ? 0 : 1; } @@ -210,6 +213,43 @@ public abstract class TreeFilter { public abstract boolean shouldBeRecursive(); /** + * Return true if the tree entries within this commit require + * {@link #include(TreeWalk)} to correctly determine whether they are + * interesting to report. + * <p> + * Otherwise, all tree entries within this commit are UNINTERESTING for this + * tree filter. + * + * @param c + * the commit being considered by the TreeFilter. + * @param rw + * the RevWalk used in retrieving relevant commit data. + * @param cpfUsed + * if not null, it reports if the changedPathFilter was used in + * this method + * @return True if the tree entries within c require + * {@link #include(TreeWalk)}. + * @since 7.3 + */ + public boolean shouldTreeWalk(RevCommit c, RevWalk rw, + @Nullable MutableBoolean cpfUsed) { + return true; + } + + /** + * If this filter checks that a specific set of paths have all been + * modified, returns that set of paths to be checked against a changed path + * filter. Otherwise, returns empty. + * + * @return a set of paths, or empty + * @deprecated use {@code shouldTreeWalk} instead. + */ + @Deprecated(since = "7.3") + public Optional<Set<byte[]>> getPathsBestEffort() { + return Optional.empty(); + } + + /** * {@inheritDoc} * * Clone this tree filter, including its parameters. @@ -220,7 +260,6 @@ public abstract class TreeFilter { @Override public abstract TreeFilter clone(); - /** {@inheritDoc} */ @Override public String toString() { String n = getClass().getName(); @@ -230,4 +269,33 @@ public abstract class TreeFilter { } return n.replace('$', '.'); } + + /** + * Mutable wrapper to return a boolean in a function parameter. + * + * @since 7.3 + */ + public static class MutableBoolean { + private boolean value; + + /** + * Return the boolean value. + * + * @return The state of the internal boolean value. + */ + public boolean get() { + return value; + } + + void orValue(boolean v) { + value = value || v; + } + + /** + * Reset the boolean value. + */ + public void reset() { + value = false; + } + } } |