diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java index cb7cad834..4c9c54f21 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, Dariusz Luksza + * Copyright (C) 2011, 2013 Dariusz Luksza * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -47,6 +47,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -65,6 +66,8 @@ import org.eclipse.jgit.treewalk.EmptyTreeIterator; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.jgit.util.FileUtils; import org.junit.Test; @@ -265,6 +268,71 @@ public void shouldListChangesInWorkingTree() throws Exception { assertThat(entry.getNewPath(), is("b.txt")); } + @Test + public void shouldMarkEntriesWhenGivenMarkTreeFilter() throws Exception { + // given + Git git = new Git(db); + RevCommit c1 = git.commit().setMessage("initial commit").call(); + FileUtils.mkdir(new File(db.getWorkTree(), "b")); + writeTrashFile("a.txt", "a"); + writeTrashFile("b/1.txt", "b1"); + writeTrashFile("b/2.txt", "b2"); + writeTrashFile("c.txt", "c"); + git.add().addFilepattern("a.txt").addFilepattern("b") + .addFilepattern("c.txt").call(); + RevCommit c2 = git.commit().setMessage("second commit").call(); + TreeFilter filterA = PathFilterGroup.createFromStrings("a.txt"); + TreeFilter filterB = PathFilterGroup.createFromStrings("b"); + TreeFilter filterB2 = PathFilterGroup.createFromStrings("b/2.txt"); + + // when + TreeWalk walk = new TreeWalk(db); + walk.addTree(c1.getTree()); + walk.addTree(c2.getTree()); + List result = DiffEntry.scan(walk, true, new TreeFilter[] { + filterA, filterB, filterB2 }); + + // then + assertThat(result, notNullValue()); + assertEquals(5, result.size()); + + DiffEntry entryA = result.get(0); + DiffEntry entryB = result.get(1); + DiffEntry entryB1 = result.get(2); + DiffEntry entryB2 = result.get(3); + DiffEntry entryC = result.get(4); + + assertThat(entryA.getNewPath(), is("a.txt")); + assertTrue(entryA.isMarked(0)); + assertFalse(entryA.isMarked(1)); + assertFalse(entryA.isMarked(2)); + assertEquals(1, entryA.getTreeFilterMarks()); + + assertThat(entryB.getNewPath(), is("b")); + assertFalse(entryB.isMarked(0)); + assertTrue(entryB.isMarked(1)); + assertTrue(entryB.isMarked(2)); + assertEquals(6, entryB.getTreeFilterMarks()); + + assertThat(entryB1.getNewPath(), is("b/1.txt")); + assertFalse(entryB1.isMarked(0)); + assertTrue(entryB1.isMarked(1)); + assertFalse(entryB1.isMarked(2)); + assertEquals(2, entryB1.getTreeFilterMarks()); + + assertThat(entryB2.getNewPath(), is("b/2.txt")); + assertFalse(entryB2.isMarked(0)); + assertTrue(entryB2.isMarked(1)); + assertTrue(entryB2.isMarked(2)); + assertEquals(6, entryB2.getTreeFilterMarks()); + + assertThat(entryC.getNewPath(), is("c.txt")); + assertFalse(entryC.isMarked(0)); + assertFalse(entryC.isMarked(1)); + assertFalse(entryC.isMarked(2)); + assertEquals(0, entryC.getTreeFilterMarks()); + } + @Test(expected = IllegalArgumentException.class) public void shouldThrowIAEWhenTreeWalkHasLessThanTwoTrees() throws Exception { diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 48cb48754..6673a8cd4 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -477,6 +477,7 @@ transportProtoLocal=Local Git Repository transportProtoSFTP=SFTP transportProtoSSH=SSH treeEntryAlreadyExists=Tree entry "{0}" already exists. +treeFilterMarkerTooManyFilters=Too many markTreeFilters passed, maximum number is {0} (passed {1}) treeIteratorDoesNotSupportRemove=TreeIterator does not support remove() treeWalkMustHaveExactlyTwoTrees=TreeWalk should have exactly two trees. truncatedHunkLinesMissingForAncestor=Truncated hunk, at least {0} lines missing for ancestor {1} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java index cc8d285d9..a3d4e09d7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010, Google Inc. + * Copyright (C) 2008-2013, Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -55,6 +55,8 @@ import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.treewalk.filter.TreeFilterMarker; /** A value class representing a change to a file */ public class DiffEntry { @@ -123,7 +125,7 @@ public static List scan(TreeWalk walk) throws IOException { * when {@code includeTrees} parameter is {@code true} it can't * be recursive. * @param includeTrees - * include tree object's. + * include tree objects. * @return headers describing the changed files. * @throws IOException * the repository cannot be accessed. @@ -134,6 +136,36 @@ public static List scan(TreeWalk walk) throws IOException { */ public static List scan(TreeWalk walk, boolean includeTrees) throws IOException { + return scan(walk, includeTrees, null); + } + + /** + * Convert the TreeWalk into DiffEntry headers, depending on + * {@code includeTrees} it will add tree objects into result or not. + * + * @param walk + * the TreeWalk to walk through. Must have exactly two trees and + * when {@code includeTrees} parameter is {@code true} it can't + * be recursive. + * @param includeTrees + * include tree objects. + * @param markTreeFilters + * array of tree filters which will be tested for each entry. If + * an entry matches, the entry will later return true when + * queried through {{@link #isMarked(int)} (with the index from + * this passed array). + * @return headers describing the changed files. + * @throws IOException + * the repository cannot be accessed. + * @throws IllegalArgumentException + * when {@code includeTrees} is true and given TreeWalk is + * recursive. Or when given TreeWalk doesn't have exactly two + * trees + * @since 2.3 + */ + public static List scan(TreeWalk walk, boolean includeTrees, + TreeFilter[] markTreeFilters) + throws IOException { if (walk.getTreeCount() != 2) throw new IllegalArgumentException( JGitText.get().treeWalkMustHaveExactlyTwoTrees); @@ -141,6 +173,12 @@ public static List scan(TreeWalk walk, boolean includeTrees) throw new IllegalArgumentException( JGitText.get().cannotBeRecursiveWhenTreesAreIncluded); + TreeFilterMarker treeFilterMarker; + if (markTreeFilters != null && markTreeFilters.length > 0) + treeFilterMarker = new TreeFilterMarker(markTreeFilters); + else + treeFilterMarker = null; + List r = new ArrayList(); MutableObjectId idBuf = new MutableObjectId(); while (walk.next()) { @@ -156,6 +194,9 @@ public static List scan(TreeWalk walk, boolean includeTrees) entry.newMode = walk.getFileMode(1); entry.newPath = entry.oldPath = walk.getPathString(); + if (treeFilterMarker != null) + entry.treeFilterMarks = treeFilterMarker.getMarks(walk); + if (entry.oldMode == FileMode.MISSING) { entry.oldPath = DiffEntry.DEV_NULL; entry.changeType = ChangeType.ADD; @@ -294,6 +335,12 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst, /** ObjectId listed on the index line for the new (post-image) */ protected AbbreviatedObjectId newId; + /** + * Bitset for marked flags of tree filters passed to + * {@link #scan(TreeWalk, boolean, TreeFilter...)} + */ + private int treeFilterMarks = 0; + /** * Get the old name associated with this file. *

@@ -396,6 +443,48 @@ public AbbreviatedObjectId getNewId() { return newId; } + /** + * Whether the mark tree filter with the specified index matched during scan + * or not, see {@link #scan(TreeWalk, boolean, TreeFilter...)}. Example: + *

+ * + *

+	 * TreeFilter filterA = ...;
+	 * TreeFilter filterB = ...;
+	 * List entries = DiffEntry.scan(walk, false, filterA, filterB);
+	 * DiffEntry entry = entries.get(0);
+	 * boolean filterAMatched = entry.isMarked(0);
+	 * boolean filterBMatched = entry.isMarked(1);
+	 * 
+ *

+ * Note that 0 corresponds to filterA because it was the first filter that + * was passed to scan. + *

+ * To query more than one flag at once, see {@link #getTreeFilterMarks()}. + * + * @param index + * the index of the tree filter to check for (must be between 0 + * and {@link Integer#SIZE}). + * + * @return true, if the tree filter matched; false if not + * @since 2.3 + */ + public boolean isMarked(int index) { + return (treeFilterMarks & (1L << index)) != 0; + } + + /** + * Get the raw tree filter marks, as set during + * {@link #scan(TreeWalk, boolean, TreeFilter...)}. See + * {@link #isMarked(int)} to query each mark individually. + * + * @return the bitset of tree filter marks + * @since 2.3 + */ + public int getTreeFilterMarks() { + return treeFilterMarks; + } + /** * Get the object id. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 287684392..d402f139f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Sasa Zivkov + * Copyright (C) 2010, 2013 Sasa Zivkov * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -538,6 +538,7 @@ public static JGitText get() { /***/ public String transportProtoSFTP; /***/ public String transportProtoSSH; /***/ public String treeEntryAlreadyExists; + /***/ public String treeFilterMarkerTooManyFilters; /***/ public String treeIteratorDoesNotSupportRemove; /***/ public String treeWalkMustHaveExactlyTwoTrees; /***/ public String truncatedHunkLinesMissingForAncestor; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java new file mode 100644 index 000000000..59515dca5 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013, Robin Stocker + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.treewalk.filter; + +import java.io.IOException; +import java.text.MessageFormat; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.StopWalkException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.treewalk.TreeWalk; + +/** + * For testing an array of {@link TreeFilter} during a {@link TreeWalk} for each + * entry and returning the result as a bitmask. + * + * @since 2.3 + */ +public class TreeFilterMarker { + + private final TreeFilter[] filters; + + /** + * Construct a TreeFilterMarker. Note that it is stateful and can only be + * used for one walk loop. + * + * @param markTreeFilters + * the filters to use for marking, must not have more elements + * than {@link Integer#SIZE}. + * @throws IllegalArgumentException + * if more tree filters are passed than possible + */ + public TreeFilterMarker(TreeFilter[] markTreeFilters) { + if (markTreeFilters.length > Integer.SIZE) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().treeFilterMarkerTooManyFilters, + Integer.valueOf(Integer.SIZE), + Integer.valueOf(markTreeFilters.length))); + } + filters = new TreeFilter[markTreeFilters.length]; + System.arraycopy(markTreeFilters, 0, filters, 0, markTreeFilters.length); + } + + /** + * Test the filters against the walk. Returns a bitmask where each bit + * represents the result of a call to {@link TreeFilter#include(TreeWalk)}, + * ordered by the index for which the tree filters were passed in the + * constructor. + * + * @param walk + * the walk from which to test the current entry + * @return the marks bitmask + * @throws MissingObjectException + * as thrown by {@link TreeFilter#include(TreeWalk)} + * @throws IncorrectObjectTypeException + * as thrown by {@link TreeFilter#include(TreeWalk)} + * @throws IOException + * as thrown by {@link TreeFilter#include(TreeWalk)} + */ + public int getMarks(TreeWalk walk) throws MissingObjectException, + IncorrectObjectTypeException, IOException { + int marks = 0; + for (int index = 0; index < filters.length; index++) { + TreeFilter filter = filters[index]; + if (filter != null) { + try { + boolean marked = filter.include(walk); + if (marked) + marks |= (1L << index); + } catch (StopWalkException e) { + // Don't check tree filter anymore, it will no longer + // match + filters[index] = null; + } + } + } + return marks; + } + +}