#230 - Improve empty folder navigation.
Empty folders are automatically skipped when browsing repository tree (similar to github "folder jumping" feature).
2 files added
4 files modified
| | |
| | | import java.util.Map.Entry;
|
| | | import java.util.regex.Pattern;
|
| | |
|
| | | import com.google.common.base.Strings;
|
| | | import org.apache.commons.io.filefilter.TrueFileFilter;
|
| | | import org.eclipse.jgit.api.CloneCommand;
|
| | | import org.eclipse.jgit.api.FetchCommand;
|
| | |
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of files in the specified folder at the specified
|
| | | * commit. If the repository does not exist or is empty, an empty list is
|
| | | * returned.
|
| | | *
|
| | | * This is modified version that implements path compression feature.
|
| | | *
|
| | | * @param repository
|
| | | * @param path
|
| | | * if unspecified, root folder is assumed.
|
| | | * @param commit
|
| | | * if null, HEAD is assumed.
|
| | | * @return list of files in specified path
|
| | | */
|
| | | public static List<PathModel> getFilesInPath2(Repository repository, String path, RevCommit commit) {
|
| | |
|
| | | List<PathModel> list = new ArrayList<PathModel>();
|
| | | if (!hasCommits(repository)) {
|
| | | return list;
|
| | | }
|
| | | if (commit == null) {
|
| | | commit = getCommit(repository, null);
|
| | | }
|
| | | final TreeWalk tw = new TreeWalk(repository);
|
| | | try {
|
| | |
|
| | | tw.addTree(commit.getTree());
|
| | | final boolean isPathEmpty = Strings.isNullOrEmpty(path);
|
| | |
|
| | | if (!isPathEmpty) {
|
| | | PathFilter f = PathFilter.create(path);
|
| | | tw.setFilter(f);
|
| | | }
|
| | |
|
| | | tw.setRecursive(true);
|
| | | List<String> paths = new ArrayList<>();
|
| | |
|
| | | while (tw.next()) {
|
| | | String child = isPathEmpty ? tw.getPathString()
|
| | | : tw.getPathString().replaceFirst(String.format("%s/", path), "");
|
| | | paths.add(child);
|
| | | }
|
| | |
|
| | | for(String p: PathUtils.compressPaths(paths)) {
|
| | | String pathString = isPathEmpty ? p : String.format("%s/%s", path, p);
|
| | | list.add(getPathModel(repository, pathString, path, commit));
|
| | | }
|
| | |
|
| | | } catch (IOException e) {
|
| | | error(e, repository, "{0} failed to get files for commit {1}", commit.getName());
|
| | | } finally {
|
| | | tw.release();
|
| | | }
|
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the list of files changed in a specified commit. If the
|
| | | * repository does not exist or is empty, an empty list is returned.
|
| | | *
|
| | |
| | | }
|
| | |
|
| | | /**
|
| | | * Returns a path model by path string
|
| | | *
|
| | | * @param repo
|
| | | * @param path
|
| | | * @param filter
|
| | | * @param commit
|
| | | * @return a path model of the specified object
|
| | | */
|
| | | private static PathModel getPathModel(Repository repo, String path, String filter, RevCommit commit)
|
| | | throws IOException {
|
| | |
|
| | | long size = 0;
|
| | | TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
|
| | | String pathString = path;
|
| | |
|
| | | if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {
|
| | | size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB);
|
| | | pathString = PathUtils.getLastPathComponent(pathString);
|
| | |
|
| | | } else if (tw.isSubtree()) {
|
| | |
|
| | | // do not display dirs that are behind in the path
|
| | | if (!Strings.isNullOrEmpty(filter)) {
|
| | | pathString = path.replaceFirst(filter + "/", "");
|
| | | }
|
| | |
|
| | | // remove the last slash from path in displayed link
|
| | | if (pathString != null && pathString.charAt(pathString.length()-1) == '/') {
|
| | | pathString = pathString.substring(0, pathString.length()-1);
|
| | | }
|
| | | }
|
| | |
|
| | | return new PathModel(pathString, tw.getPathString(), size, tw.getFileMode(0).getBits(),
|
| | | tw.getObjectId(0).getName(), commit.getName());
|
| | |
|
| | |
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * Returns a permissions representation of the mode bits.
|
| | | *
|
| | | * @param mode
|
New file |
| | |
| | | package com.gitblit.utils; |
| | | |
| | | import com.google.common.base.Joiner; |
| | | import com.google.common.base.Splitter; |
| | | import com.google.common.collect.Iterables; |
| | | |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * Utils for handling path strings |
| | | * |
| | | */ |
| | | public class PathUtils { |
| | | |
| | | private PathUtils() {} |
| | | |
| | | /** |
| | | * Compress paths containing no files |
| | | * |
| | | * @param paths lines from `git ls-tree -r --name-only ${branch}` |
| | | * @return compressed paths |
| | | */ |
| | | public static List<String> compressPaths(final Iterable<String> paths) { |
| | | |
| | | ArrayList<String> pathList = new ArrayList<>(); |
| | | Map<String, List<String[]>> folderRoots = new LinkedHashMap<>(); |
| | | |
| | | for (String s: paths) { |
| | | String[] components = s.split("/"); |
| | | |
| | | // File in current directory |
| | | if (components.length == 1) { |
| | | pathList.add(components[0]); |
| | | |
| | | // Directory path |
| | | } else { |
| | | List<String[]> rootedPaths = folderRoots.get(components[0]); |
| | | if (rootedPaths == null) { |
| | | rootedPaths = new ArrayList<>(); |
| | | } |
| | | rootedPaths.add(components); |
| | | folderRoots.put(components[0], rootedPaths); |
| | | } |
| | | } |
| | | |
| | | for (String folder: folderRoots.keySet()) { |
| | | List<String[]> matchingPaths = folderRoots.get(folder); |
| | | |
| | | if (matchingPaths.size() == 1) { |
| | | pathList.add(toStringPath(matchingPaths.get(0))); |
| | | } else { |
| | | pathList.add(longestCommonSequence(matchingPaths)); |
| | | } |
| | | } |
| | | return pathList; |
| | | } |
| | | |
| | | /** |
| | | * Get last path component |
| | | * |
| | | * |
| | | * @param path string path separated by slashes |
| | | * @return rightmost entry |
| | | */ |
| | | public static String getLastPathComponent(final String path) { |
| | | return Iterables.getLast(Splitter.on("/").omitEmptyStrings().split(path), path); |
| | | } |
| | | |
| | | private static String toStringPath(final String[] pathComponents) { |
| | | List<String> tmp = Arrays.asList(pathComponents); |
| | | return Joiner.on('/').join(tmp.subList(0,tmp.size()-1)) + '/'; |
| | | } |
| | | |
| | | |
| | | private static String longestCommonSequence(final List<String[]> paths) { |
| | | |
| | | StringBuilder path = new StringBuilder(); |
| | | |
| | | for (int i = 0; i < paths.get(0).length; i++) { |
| | | String current = paths.get(0)[i]; |
| | | for (int j = 1; j < paths.size(); j++) { |
| | | if (!current.equals(paths.get(j)[i])) { |
| | | return path.toString(); |
| | | } |
| | | } |
| | | path.append(current); |
| | | path.append('/'); |
| | | } |
| | | return path.toString(); |
| | | } |
| | | |
| | | } |
| | |
| | |
|
| | | Repository r = getRepository();
|
| | | RevCommit commit = getCommit();
|
| | | List<PathModel> paths = JGitUtils.getFilesInPath(r, path, commit);
|
| | | List<PathModel> paths = JGitUtils.getFilesInPath2(r, path, commit);
|
| | |
|
| | | // tree page links
|
| | | add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
|
| | |
| | | FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,
|
| | | ModelUtilsTest.class, JnaUtilsTest.class, LdapSyncServiceTest.class, FileTicketServiceTest.class, |
| | | BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class,
|
| | | SshKeysDispatcherTest.class, UITicketTest.class }) |
| | | SshKeysDispatcherTest.class, UITicketTest.class, PathUtilsTest.class })
|
| | | public class GitBlitSuite {
|
| | |
|
| | | public static final File BASEFOLDER = new File("data");
|
| | |
| | | }
|
| | |
|
| | | @Test
|
| | | public void testFilesInPath2() throws Exception {
|
| | | assertEquals(0, JGitUtils.getFilesInPath2(null, null, null).size());
|
| | | Repository repository = GitBlitSuite.getHelloworldRepository();
|
| | | List<PathModel> files = JGitUtils.getFilesInPath2(repository, null, null);
|
| | | repository.close();
|
| | | assertTrue(files.size() > 10);
|
| | | }
|
| | |
|
| | | @Test
|
| | | public void testDocuments() throws Exception {
|
| | | Repository repository = GitBlitSuite.getTicgitRepository();
|
| | | List<String> extensions = Arrays.asList(new String[] { ".mkd", ".md" });
|
New file |
| | |
| | | /* |
| | | * Copyright 2011 gitblit.com. |
| | | * |
| | | * Licensed under the Apache License, Version 2.0 (the "License"); |
| | | * you may not use this file except in compliance with the License. |
| | | * You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, software |
| | | * distributed under the License is distributed on an "AS IS" BASIS, |
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | * See the License for the specific language governing permissions and |
| | | * limitations under the License. |
| | | */ |
| | | package com.gitblit.tests; |
| | | |
| | | import com.gitblit.utils.PathUtils; |
| | | import org.junit.Test; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | public class PathUtilsTest extends GitblitUnitTest { |
| | | |
| | | private static final String[][][] testData = { |
| | | |
| | | { |
| | | // Folder contents |
| | | {".gitignore","src/main/java/a.java", "src/main/java/b.java", "docs/c.md"}, |
| | | // Expected after compressing |
| | | {".gitignore", "src/main/java/", "docs/"} |
| | | }, |
| | | |
| | | { |
| | | {".gitignore","src/main/java/a.java", "src/main/b.java", "docs/c.md"}, |
| | | {".gitignore", "src/main/", "docs/"} |
| | | }, |
| | | |
| | | { |
| | | {".gitignore","src/x.java","src/main/java/a.java", "src/main/java/b.java", "docs/c.md"}, |
| | | {".gitignore", "src/", "docs/"} |
| | | }, |
| | | }; |
| | | |
| | | |
| | | |
| | | |
| | | @Test |
| | | public void testCompressPaths() throws Exception { |
| | | |
| | | for (String[][] test : testData ) { |
| | | assertArrayEquals(test[1], PathUtils.compressPaths(Arrays.asList(test[0])).toArray(new String[]{})); |
| | | } |
| | | |
| | | } |
| | | |
| | | @Test |
| | | public void testGetLastPathComponent() { |
| | | assertEquals(PathUtils.getLastPathComponent("/a/b/c/d/e.out"), "e.out"); |
| | | assertEquals(PathUtils.getLastPathComponent("e.out"), "e.out"); |
| | | assertEquals(PathUtils.getLastPathComponent("/a/b/c/d/"), "d"); |
| | | assertEquals(PathUtils.getLastPathComponent("/a/b/c/d"), "d"); |
| | | assertEquals(PathUtils.getLastPathComponent("/"), "/"); |
| | | } |
| | | |
| | | } |