Enable marking entries using TreeFilters in DiffEntry

This adds a new optional TreeFilter[] argument to DiffEntry.scan. All
filters will be checked during the scan to determine if an entry should
be "marked" with regard to that filter.

After having called scan, the user can then call isMarked(int) on the
entries to find out whether they matched the TreeFilter with the passed
index.

An example use case for this is in the file diff viewer of EGit's
History view, where we'd like to highlight entries that are matching the
current filter.

See EGit change I03da4b38d1591495cb290909f0e4c6e52270e97f.

Bug: 393610
Change-Id: Icf911fe6fca131b2567514f54d66636a44561af1
Signed-off-by: Robin Stocker <robin@nibor.org>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Robin Stocker 2012-06-26 00:55:39 +02:00 committed by Matthias Sohn
parent 35d4749011
commit 75ddf2a0f4
5 changed files with 285 additions and 4 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011, Dariusz Luksza <dariusz@luksza.org>
* Copyright (C) 2011, 2013 Dariusz Luksza <dariusz@luksza.org>
* 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<DiffEntry> 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 {

View File

@ -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}

View File

@ -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<DiffEntry> 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<DiffEntry> scan(TreeWalk walk) throws IOException {
*/
public static List<DiffEntry> 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<DiffEntry> 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<DiffEntry> 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<DiffEntry> r = new ArrayList<DiffEntry>();
MutableObjectId idBuf = new MutableObjectId();
while (walk.next()) {
@ -156,6 +194,9 @@ public static List<DiffEntry> 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.
* <p>
@ -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:
* <p>
*
* <pre>
* TreeFilter filterA = ...;
* TreeFilter filterB = ...;
* List<DiffEntry> entries = DiffEntry.scan(walk, false, filterA, filterB);
* DiffEntry entry = entries.get(0);
* boolean filterAMatched = entry.isMarked(0);
* boolean filterBMatched = entry.isMarked(1);
* </pre>
* <p>
* Note that 0 corresponds to filterA because it was the first filter that
* was passed to scan.
* <p>
* 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.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com>
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
* 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;

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2013, Robin Stocker <robin@nibor.org>
* 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;
}
}