From 252dc07d7f85cc344b5919bb7c6166ef84b2102e Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gmail.com> Date: Mon, 25 Jan 2016 11:55:58 -0500 Subject: [PATCH] Merge pull request #988 from gitblit/976-raw-download-filestore-item --- src/main/java/com/gitblit/wicket/pages/TreePage.java | 44 +++- src/main/java/com/gitblit/models/PathModel.java | 56 ++++ src/main/java/com/gitblit/Constants.java | 4 src/test/java/com/gitblit/tests/JGitUtilsTest.java | 6 src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java | 5 src/main/java/com/gitblit/wicket/panels/HistoryPanel.java | 2 src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java | 7 src/main/java/com/gitblit/wicket/pages/BlamePage.java | 50 +++- src/main/java/com/gitblit/utils/CompressionUtils.java | 92 +++++++- src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java | 31 ++ src/main/java/com/gitblit/servlet/GitFilter.java | 14 src/main/java/com/gitblit/wicket/pages/CommitPage.html | 3 src/main/java/com/gitblit/wicket/pages/TreePage.html | 3 src/main/java/com/gitblit/utils/JGitUtils.java | 130 ++++++++++-- src/main/java/com/gitblit/wicket/pages/FilestorePage.java | 1 src/main/java/com/gitblit/models/FilestoreModel.java | 67 ++++++ src/main/java/com/gitblit/servlet/FilestoreServlet.java | 5 src/main/java/com/gitblit/utils/DiffStatFormatter.java | 5 src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html | 1 src/main/java/com/gitblit/servlet/RawServlet.java | 2 src/main/resources/gitblit.css | 6 src/main/java/com/gitblit/wicket/pages/CommitPage.java | 36 ++ src/main/java/com/gitblit/utils/DiffUtils.java | 11 src/main/java/com/gitblit/servlet/DownloadZipServlet.java | 18 + 24 files changed, 468 insertions(+), 131 deletions(-) diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java index 0a99953..6232552 100644 --- a/src/main/java/com/gitblit/Constants.java +++ b/src/main/java/com/gitblit/Constants.java @@ -94,6 +94,10 @@ public static final int LEN_SHORTLOG = 78; public static final int LEN_SHORTLOG_REFS = 60; + + public static final int LEN_FILESTORE_META_MIN = 125; + + public static final int LEN_FILESTORE_META_MAX = 146; public static final String DEFAULT_BRANCH = "default"; diff --git a/src/main/java/com/gitblit/models/FilestoreModel.java b/src/main/java/com/gitblit/models/FilestoreModel.java index 4144df6..2ed1ede 100644 --- a/src/main/java/com/gitblit/models/FilestoreModel.java +++ b/src/main/java/com/gitblit/models/FilestoreModel.java @@ -20,6 +20,10 @@ import java.util.Date; import java.util.List; import java.util.NoSuchElementException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.gitblit.Constants; /** * A FilestoreModel represents a file stored outside a repository but referenced by the repository using a unique objectID @@ -31,6 +35,18 @@ private static final long serialVersionUID = 1L; + private static final String metaRegexText = new StringBuilder() + .append("version\\shttps://git-lfs.github.com/spec/v1\\s+") + .append("oid\\ssha256:(" + Constants.REGEX_SHA256 + ")\\s+") + .append("size\\s([0-9]+)") + .toString(); + + private static final Pattern metaRegex = Pattern.compile(metaRegexText); + + private static final int metaRegexIndexSHA = 1; + + private static final int metaRegexIndexSize = 2; + public final String oid; private Long size; @@ -43,6 +59,12 @@ //Access Control private List<String> repositories; + public FilestoreModel(String id, long definedSize) { + oid = id; + size = definedSize; + status = Status.ReferenceOnly; + } + public FilestoreModel(String id, long expectedSize, UserModel user, String repo) { oid = id; size = expectedSize; @@ -51,6 +73,29 @@ stateChangedOn = new Date(); repositories = new ArrayList<String>(); repositories.add(repo); + } + + /* + * Attempts to create a FilestoreModel from the given meta string + * + * @return A valid FilestoreModel if successful, otherwise null + */ + public static FilestoreModel fromMetaString(String meta) { + + Matcher m = metaRegex.matcher(meta); + + if (m.find()) { + try + { + final Long size = Long.parseLong(m.group(metaRegexIndexSize)); + final String sha = m.group(metaRegexIndexSHA); + return new FilestoreModel(sha, size); + } catch (Exception e) { + //Fail silent - it is not a valid filestore item + } + } + + return null; } public synchronized long getSize() { @@ -102,19 +147,25 @@ } public synchronized void addRepository(String repo) { - if (!repositories.contains(repo)) { - repositories.add(repo); - } + if (status != Status.ReferenceOnly) { + if (!repositories.contains(repo)) { + repositories.add(repo); + } + } } public synchronized void removeRepository(String repo) { - repositories.remove(repo); + if (status != Status.ReferenceOnly) { + repositories.remove(repo); + } } public synchronized boolean isInRepositoryList(List<String> repoList) { - for (String name : repositories) { - if (repoList.contains(name)) { - return true; + if (status != Status.ReferenceOnly) { + for (String name : repositories) { + if (repoList.contains(name)) { + return true; + } } } return false; @@ -122,6 +173,8 @@ public static enum Status { + ReferenceOnly(-42), + Deleted(-30), AuthenticationRequired(-20), diff --git a/src/main/java/com/gitblit/models/PathModel.java b/src/main/java/com/gitblit/models/PathModel.java index bf58542..3c280eb 100644 --- a/src/main/java/com/gitblit/models/PathModel.java +++ b/src/main/java/com/gitblit/models/PathModel.java @@ -15,11 +15,20 @@ */ package com.gitblit.models; +import java.io.IOException; import java.io.Serializable; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry.ChangeType; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevWalk; + +import com.gitblit.manager.FilestoreManager; +import com.gitblit.utils.JGitUtils; /** * PathModel is a serializable model class that represents a file or a folder, @@ -34,16 +43,18 @@ public final String name; public final String path; + private final FilestoreModel filestoreItem; public final long size; public final int mode; public final String objectId; public final String commitId; public boolean isParentPath; - - public PathModel(String name, String path, long size, int mode, String objectId, String commitId) { + + public PathModel(String name, String path, FilestoreModel filestoreItem, long size, int mode, String objectId, String commitId) { this.name = name; this.path = path; - this.size = size; + this.filestoreItem = filestoreItem; + this.size = (filestoreItem == null) ? size : filestoreItem.getSize(); this.mode = mode; this.objectId = objectId; this.commitId = commitId; @@ -65,6 +76,18 @@ return FileMode.REGULAR_FILE.equals(mode) || FileMode.EXECUTABLE_FILE.equals(mode) || (FileMode.MISSING.equals(mode) && !isSymlink() && !isSubmodule() && !isTree()); + } + + public boolean isFilestoreItem() { + return filestoreItem != null; + } + + public String getFilestoreOid() { + if (filestoreItem != null) { + return filestoreItem.oid; + } + + return null; } @Override @@ -119,9 +142,9 @@ public int deletions; - public PathChangeModel(String name, String path, long size, int mode, String objectId, + public PathChangeModel(String name, String path, FilestoreModel filestoreItem, long size, int mode, String objectId, String commitId, ChangeType type) { - super(name, path, size, mode, objectId, commitId); + super(name, path, filestoreItem, size, mode, objectId, commitId); this.changeType = type; } @@ -148,18 +171,33 @@ return super.equals(o); } - public static PathChangeModel from(DiffEntry diff, String commitId) { + public static PathChangeModel from(DiffEntry diff, String commitId, Repository repository) { PathChangeModel pcm; + FilestoreModel filestoreItem = null; + long size = 0; + + if (repository != null) { + try (RevWalk revWalk = new RevWalk(repository)) { + size = revWalk.getObjectReader().getObjectSize(diff.getNewId().toObjectId(), Constants.OBJ_BLOB); + + if (JGitUtils.isPossibleFilestoreItem(size)) { + filestoreItem = JGitUtils.getFilestoreItem(revWalk.getObjectReader().open(diff.getNewId().toObjectId())); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (diff.getChangeType().equals(ChangeType.DELETE)) { - pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff + pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), filestoreItem, size, diff .getNewMode().getBits(), diff.getOldId().name(), commitId, diff .getChangeType()); } else if (diff.getChangeType().equals(ChangeType.RENAME)) { - pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff + pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), filestoreItem, size, diff .getNewMode().getBits(), diff.getNewId().name(), commitId, diff .getChangeType()); } else { - pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff + pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), filestoreItem, size, diff .getNewMode().getBits(), diff.getNewId().name(), commitId, diff .getChangeType()); } diff --git a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java index b9cb088..e1d76db 100644 --- a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java +++ b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java @@ -133,10 +133,11 @@ /** * Allows authentication header to be altered based on the action requested * Default is WWW-Authenticate + * @param httpRequest * @param action * @return authentication type header */ - protected String getAuthenticationHeader(String action) { + protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) { return "WWW-Authenticate"; } @@ -192,7 +193,7 @@ logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl)); } - httpResponse.setHeader(getAuthenticationHeader(urlRequestType), CHALLENGE); + httpResponse.setHeader(getAuthenticationHeader(httpRequest, urlRequestType), CHALLENGE); httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } else { @@ -239,7 +240,7 @@ if (runtimeManager.isDebugMode()) { logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl)); } - httpResponse.setHeader(getAuthenticationHeader(urlRequestType), CHALLENGE); + httpResponse.setHeader(getAuthenticationHeader(httpRequest, urlRequestType), CHALLENGE); httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } else { diff --git a/src/main/java/com/gitblit/servlet/DownloadZipServlet.java b/src/main/java/com/gitblit/servlet/DownloadZipServlet.java index 0756256..319c4f9 100644 --- a/src/main/java/com/gitblit/servlet/DownloadZipServlet.java +++ b/src/main/java/com/gitblit/servlet/DownloadZipServlet.java @@ -22,6 +22,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; + import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletResponse; @@ -34,6 +35,7 @@ import com.gitblit.Constants; import com.gitblit.IStoredSettings; import com.gitblit.Keys; +import com.gitblit.manager.IFilestoreManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.utils.CompressionUtils; import com.gitblit.utils.JGitUtils; @@ -57,6 +59,8 @@ private IStoredSettings settings; private IRepositoryManager repositoryManager; + + private IFilestoreManager filestoreManager; public static enum Format { zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2"); @@ -78,9 +82,10 @@ } @Inject - public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager) { + public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager, IFilestoreManager filestoreManager) { this.settings = settings; this.repositoryManager = repositoryManager; + this.filestoreManager = filestoreManager; } /** @@ -169,22 +174,23 @@ response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); + try { switch (format) { case zip: - CompressionUtils.zip(r, basePath, objectId, response.getOutputStream()); + CompressionUtils.zip(r, filestoreManager, basePath, objectId, response.getOutputStream()); break; case tar: - CompressionUtils.tar(r, basePath, objectId, response.getOutputStream()); + CompressionUtils.tar(r, filestoreManager, basePath, objectId, response.getOutputStream()); break; case gz: - CompressionUtils.gz(r, basePath, objectId, response.getOutputStream()); + CompressionUtils.gz(r, filestoreManager, basePath, objectId, response.getOutputStream()); break; case xz: - CompressionUtils.xz(r, basePath, objectId, response.getOutputStream()); + CompressionUtils.xz(r, filestoreManager, basePath, objectId, response.getOutputStream()); break; case bzip2: - CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream()); + CompressionUtils.bzip2(r, filestoreManager, basePath, objectId, response.getOutputStream()); break; } diff --git a/src/main/java/com/gitblit/servlet/FilestoreServlet.java b/src/main/java/com/gitblit/servlet/FilestoreServlet.java index 8f303fc..4af9084 100644 --- a/src/main/java/com/gitblit/servlet/FilestoreServlet.java +++ b/src/main/java/com/gitblit/servlet/FilestoreServlet.java @@ -238,7 +238,10 @@ } } else { response.setStatus(responseObject.error.code); - serialize(response, responseObject.error); + + if (isMetaRequest) { + serialize(response, responseObject.error); + } } }; diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java index 27408f0..9522893 100644 --- a/src/main/java/com/gitblit/servlet/GitFilter.java +++ b/src/main/java/com/gitblit/servlet/GitFilter.java @@ -102,8 +102,8 @@ } /** - * Analyze the url and returns the action of the request. Return values are - * either "/git-receive-pack" or "/git-upload-pack". + * Analyze the url and returns the action of the request. Return values are: + * "/git-receive-pack", "/git-upload-pack" or "/info/lfs". * * @param serverUrl * @return action of the request @@ -316,18 +316,22 @@ /** * Git lfs action uses an alternative authentication header, + * dependent on the viewing method. * + * @param httpRequest * @param action * @return */ @Override - protected String getAuthenticationHeader(String action) { + protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) { if (action.equals(gitLfs)) { - return "LFS-Authenticate"; + if (hasContentInRequestHeader(httpRequest, "Accept", FilestoreServlet.GIT_LFS_META_MIME)) { + return "LFS-Authenticate"; + } } - return super.getAuthenticationHeader(action); + return super.getAuthenticationHeader(httpRequest, action); } /** diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java index 897047d..dca5773 100644 --- a/src/main/java/com/gitblit/servlet/RawServlet.java +++ b/src/main/java/com/gitblit/servlet/RawServlet.java @@ -364,7 +364,7 @@ if (pathEntries.get(0).path.indexOf('/') > -1) { // we are in a subdirectory, add parent directory link String pp = URLEncoder.encode(requestedPath, Constants.ENCODING); - pathEntries.add(0, new PathModel("..", pp + "/..", 0, FileMode.TREE.getBits(), null, null)); + pathEntries.add(0, new PathModel("..", pp + "/..", null, 0, FileMode.TREE.getBits(), null, null)); } } diff --git a/src/main/java/com/gitblit/utils/CompressionUtils.java b/src/main/java/com/gitblit/utils/CompressionUtils.java index b06edd2..1b8e6fc 100644 --- a/src/main/java/com/gitblit/utils/CompressionUtils.java +++ b/src/main/java/com/gitblit/utils/CompressionUtils.java @@ -16,6 +16,9 @@ package com.gitblit.utils; import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.text.MessageFormat; @@ -28,9 +31,11 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.compressors.CompressorException; import org.apache.commons.compress.compressors.CompressorStreamFactory; +import org.apache.commons.io.IOUtils; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; @@ -40,6 +45,11 @@ import org.eclipse.jgit.treewalk.filter.PathFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.gitblit.GitBlit; +import com.gitblit.manager.IFilestoreManager; +import com.gitblit.models.FilestoreModel; +import com.gitblit.models.FilestoreModel.Status; /** * Collection of static methods for retrieving information from a repository. @@ -87,7 +97,7 @@ * @return true if repository was successfully zipped to supplied output * stream */ - public static boolean zip(Repository repository, String basePath, String objectId, + public static boolean zip(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId, OutputStream os) { RevCommit commit = JGitUtils.getCommit(repository, objectId); if (commit == null) { @@ -115,16 +125,40 @@ continue; } tw.getObjectId(id, 0); - + + ObjectLoader loader = repository.open(id); + ZipArchiveEntry entry = new ZipArchiveEntry(tw.getPathString()); - entry.setSize(reader.getObjectSize(id, Constants.OBJ_BLOB)); + + FilestoreModel filestoreItem = null; + + if (JGitUtils.isPossibleFilestoreItem(loader.getSize())) { + filestoreItem = JGitUtils.getFilestoreItem(tw.getObjectReader().open(id)); + } + + final long size = (filestoreItem == null) ? loader.getSize() : filestoreItem.getSize(); + + entry.setSize(size); entry.setComment(commit.getName()); entry.setUnixMode(mode.getBits()); entry.setTime(modified); zos.putArchiveEntry(entry); + + if (filestoreItem == null) { + //Copy repository stored file + loader.copyTo(zos); + } else { + //Copy filestore file + try (FileInputStream streamIn = new FileInputStream(filestoreManager.getStoragePath(filestoreItem.oid))) { + IOUtils.copyLarge(streamIn, zos); + } catch (Throwable e) { + LOGGER.error(MessageFormat.format("Failed to archive filestore item {0}", filestoreItem.oid), e); - ObjectLoader ldr = repository.open(id); - ldr.copyTo(zos); + //Handle as per other errors + throw e; + } + } + zos.closeArchiveEntry(); } zos.finish(); @@ -151,9 +185,9 @@ * @return true if repository was successfully zipped to supplied output * stream */ - public static boolean tar(Repository repository, String basePath, String objectId, + public static boolean tar(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId, OutputStream os) { - return tar(null, repository, basePath, objectId, os); + return tar(null, repository, filestoreManager, basePath, objectId, os); } /** @@ -169,9 +203,9 @@ * @return true if repository was successfully zipped to supplied output * stream */ - public static boolean gz(Repository repository, String basePath, String objectId, + public static boolean gz(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId, OutputStream os) { - return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os); + return tar(CompressorStreamFactory.GZIP, repository, filestoreManager, basePath, objectId, os); } /** @@ -187,9 +221,9 @@ * @return true if repository was successfully zipped to supplied output * stream */ - public static boolean xz(Repository repository, String basePath, String objectId, + public static boolean xz(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId, OutputStream os) { - return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os); + return tar(CompressorStreamFactory.XZ, repository, filestoreManager, basePath, objectId, os); } /** @@ -205,10 +239,10 @@ * @return true if repository was successfully zipped to supplied output * stream */ - public static boolean bzip2(Repository repository, String basePath, String objectId, + public static boolean bzip2(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId, OutputStream os) { - return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os); + return tar(CompressorStreamFactory.BZIP2, repository, filestoreManager, basePath, objectId, os); } /** @@ -227,7 +261,7 @@ * @return true if repository was successfully zipped to supplied output * stream */ - private static boolean tar(String algorithm, Repository repository, String basePath, String objectId, + private static boolean tar(String algorithm, Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId, OutputStream os) { RevCommit commit = JGitUtils.getCommit(repository, objectId); if (commit == null) { @@ -263,6 +297,7 @@ if (mode == FileMode.GITLINK || mode == FileMode.TREE) { continue; } + tw.getObjectId(id, 0); ObjectLoader loader = repository.open(id); @@ -278,9 +313,34 @@ TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString()); entry.setMode(mode.getBits()); entry.setModTime(modified); - entry.setSize(loader.getSize()); + + FilestoreModel filestoreItem = null; + + if (JGitUtils.isPossibleFilestoreItem(loader.getSize())) { + filestoreItem = JGitUtils.getFilestoreItem(tw.getObjectReader().open(id)); + } + + final long size = (filestoreItem == null) ? loader.getSize() : filestoreItem.getSize(); + + entry.setSize(size); tos.putArchiveEntry(entry); - loader.copyTo(tos); + + if (filestoreItem == null) { + //Copy repository stored file + loader.copyTo(tos); + } else { + //Copy filestore file + try (FileInputStream streamIn = new FileInputStream(filestoreManager.getStoragePath(filestoreItem.oid))) { + + IOUtils.copyLarge(streamIn, tos); + } catch (Throwable e) { + LOGGER.error(MessageFormat.format("Failed to archive filestore item {0}", filestoreItem.oid), e); + + //Handle as per other errors + throw e; + } + } + tos.closeArchiveEntry(); } } diff --git a/src/main/java/com/gitblit/utils/DiffStatFormatter.java b/src/main/java/com/gitblit/utils/DiffStatFormatter.java index 572046e..a6c3ae2 100644 --- a/src/main/java/com/gitblit/utils/DiffStatFormatter.java +++ b/src/main/java/com/gitblit/utils/DiffStatFormatter.java @@ -20,6 +20,7 @@ import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawText; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.io.NullOutputStream; import com.gitblit.models.PathModel.PathChangeModel; @@ -37,9 +38,9 @@ private PathChangeModel path; - public DiffStatFormatter(String commitId) { + public DiffStatFormatter(String commitId, Repository repository) { super(NullOutputStream.INSTANCE); - diffStat = new DiffStat(commitId); + diffStat = new DiffStat(commitId, repository); } @Override diff --git a/src/main/java/com/gitblit/utils/DiffUtils.java b/src/main/java/com/gitblit/utils/DiffUtils.java index cdebec1..41aab4c 100644 --- a/src/main/java/com/gitblit/utils/DiffUtils.java +++ b/src/main/java/com/gitblit/utils/DiffUtils.java @@ -157,13 +157,16 @@ public final List<PathChangeModel> paths = new ArrayList<PathChangeModel>(); private final String commitId; + + private final Repository repository; - public DiffStat(String commitId) { + public DiffStat(String commitId, Repository repository) { this.commitId = commitId; + this.repository = repository; } public PathChangeModel addPath(DiffEntry entry) { - PathChangeModel pcm = PathChangeModel.from(entry, commitId); + PathChangeModel pcm = PathChangeModel.from(entry, commitId, repository); paths.add(pcm); return pcm; } @@ -379,7 +382,7 @@ DiffFormatter df; switch (outputType) { case HTML: - df = new GitBlitDiffFormatter(commit.getName(), path, handler, tabLength); + df = new GitBlitDiffFormatter(commit.getName(), repository, path, handler, tabLength); break; case PLAIN: default: @@ -548,7 +551,7 @@ DiffStat stat = null; try { RawTextComparator cmp = RawTextComparator.DEFAULT; - DiffStatFormatter df = new DiffStatFormatter(commit.getName()); + DiffStatFormatter df = new DiffStatFormatter(commit.getName(), repository); df.setRepository(repository); df.setDiffComparator(cmp); df.setDetectRenames(true); diff --git a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java index 86b7ca2..8ebadbf 100644 --- a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java +++ b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java @@ -33,6 +33,7 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawText; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.RawParseUtils; import com.gitblit.models.PathModel.PathChangeModel; @@ -164,11 +165,11 @@ } - public GitBlitDiffFormatter(String commitId, String path, BinaryDiffHandler handler, int tabLength) { + public GitBlitDiffFormatter(String commitId, Repository repository, String path, BinaryDiffHandler handler, int tabLength) { super(new DiffOutputStream()); this.os = (DiffOutputStream) getOutputStream(); this.os.setFormatter(this, handler); - this.diffStat = new DiffStat(commitId); + this.diffStat = new DiffStat(commitId, repository); this.tabLength = tabLength; // If we have a full commitdiff, install maxima to avoid generating a super-long diff listing that // will only tax the browser too much. diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java index c3d0207..7a00813 100644 --- a/src/main/java/com/gitblit/utils/JGitUtils.java +++ b/src/main/java/com/gitblit/utils/JGitUtils.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.filefilter.TrueFileFilter; @@ -42,6 +43,7 @@ import org.eclipse.jgit.diff.RawTextComparator; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.LargeObjectException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.lib.BlobBasedConfig; @@ -84,12 +86,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.GitBlit; import com.gitblit.GitBlitException; +import com.gitblit.manager.GitblitManager; +import com.gitblit.models.FilestoreModel; import com.gitblit.models.GitNote; import com.gitblit.models.PathModel; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; import com.gitblit.models.SubmoduleModel; +import com.gitblit.servlet.FilestoreServlet; import com.google.common.base.Strings; /** @@ -993,23 +999,40 @@ tw.setRecursive(true); tw.addTree(commit.getTree()); while (tw.next()) { - list.add(new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw - .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(), + long size = 0; + FilestoreModel filestoreItem = null; + ObjectId objectId = tw.getObjectId(0); + + try { + if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) { + + size = tw.getObjectReader().getObjectSize(objectId, Constants.OBJ_BLOB); + + if (isPossibleFilestoreItem(size)) { + filestoreItem = getFilestoreItem(tw.getObjectReader().open(objectId)); + } + } + } catch (Throwable t) { + error(t, null, "failed to retrieve blob size for " + tw.getPathString()); + } + + list.add(new PathChangeModel(tw.getPathString(), tw.getPathString(),filestoreItem, size, tw + .getRawMode(0), objectId.getName(), commit.getId().getName(), ChangeType.ADD)); } tw.close(); } else { RevCommit parent = rw.parseCommit(commit.getParent(0).getId()); - DiffStatFormatter df = new DiffStatFormatter(commit.getName()); + DiffStatFormatter df = new DiffStatFormatter(commit.getName(), repository); df.setRepository(repository); df.setDiffComparator(RawTextComparator.DEFAULT); df.setDetectRenames(true); List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree()); for (DiffEntry diff : diffs) { // create the path change model - PathChangeModel pcm = PathChangeModel.from(diff, commit.getName()); - - if (calculateDiffStat) { + PathChangeModel pcm = PathChangeModel.from(diff, commit.getName(), repository); + + if (calculateDiffStat) { // update file diffstats df.format(diff); PathChangeModel pathStat = df.getDiffStat().getPath(pcm.path); @@ -1083,7 +1106,7 @@ List<DiffEntry> diffEntries = df.scan(startCommit.getTree(), endCommit.getTree()); for (DiffEntry diff : diffEntries) { - PathChangeModel pcm = PathChangeModel.from(diff, endCommit.getName()); + PathChangeModel pcm = PathChangeModel.from(diff, endCommit.getName(), repository); list.add(pcm); } Collections.sort(list); @@ -1167,21 +1190,54 @@ private static PathModel getPathModel(TreeWalk tw, String basePath, RevCommit commit) { String name; long size = 0; + if (StringUtils.isEmpty(basePath)) { name = tw.getPathString(); } else { name = tw.getPathString().substring(basePath.length() + 1); } ObjectId objectId = tw.getObjectId(0); + FilestoreModel filestoreItem = null; + try { if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) { + size = tw.getObjectReader().getObjectSize(objectId, Constants.OBJ_BLOB); + + if (isPossibleFilestoreItem(size)) { + filestoreItem = getFilestoreItem(tw.getObjectReader().open(objectId)); + } } } catch (Throwable t) { error(t, null, "failed to retrieve blob size for " + tw.getPathString()); } - return new PathModel(name, tw.getPathString(), size, tw.getFileMode(0).getBits(), + return new PathModel(name, tw.getPathString(), filestoreItem, size, tw.getFileMode(0).getBits(), objectId.getName(), commit.getName()); + } + + public static boolean isPossibleFilestoreItem(long size) { + return ( (size >= com.gitblit.Constants.LEN_FILESTORE_META_MIN) + && (size <= com.gitblit.Constants.LEN_FILESTORE_META_MAX)); + } + + /** + * + * @return Representative FilestoreModel if valid, otherwise null + */ + public static FilestoreModel getFilestoreItem(ObjectLoader obj){ + try { + final byte[] blob = obj.getCachedBytes(com.gitblit.Constants.LEN_FILESTORE_META_MAX); + final String meta = new String(blob, "UTF-8"); + + return FilestoreModel.fromMetaString(meta); + + } catch (LargeObjectException e) { + //Intentionally failing silent + } catch (Exception e) { + error(e, null, "failed to retrieve filestoreItem " + obj.toString()); + } + + return null; } /** @@ -1197,29 +1253,34 @@ throws IOException { long size = 0; + FilestoreModel filestoreItem = null; 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); + if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) { - } else if (tw.isSubtree()) { + pathString = PathUtils.getLastPathComponent(pathString); + + size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB); + + if (isPossibleFilestoreItem(size)) { + filestoreItem = getFilestoreItem(tw.getObjectReader().open(tw.getObjectId(0))); + } + } 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); - } + // do not display dirs that are behind in the path + if (!Strings.isNullOrEmpty(filter)) { + pathString = path.replaceFirst(filter + "/", ""); } - return new PathModel(pathString, tw.getPathString(), size, tw.getFileMode(0).getBits(), - tw.getObjectId(0).getName(), commit.getName()); + // 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(), filestoreItem, size, tw.getFileMode(0).getBits(), + tw.getObjectId(0).getName(), commit.getName()); } @@ -2513,4 +2574,27 @@ } return new MergeResult(MergeStatus.FAILED, null); } + + + /** + * Returns the LFS URL for the given oid + * Currently assumes that the Gitblit Filestore is used + * + * @param baseURL + * @param repository name + * @param oid of lfs item + * @return the lfs item URL + */ + public static String getLfsRepositoryUrl(String baseURL, String repositoryName, String oid) { + + if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { + baseURL = baseURL.substring(0, baseURL.length() - 1); + } + + return baseURL + com.gitblit.Constants.R_PATH + + repositoryName + "/" + + com.gitblit.Constants.R_LFS + + "objects/" + oid; + + } } diff --git a/src/main/java/com/gitblit/wicket/pages/BlamePage.java b/src/main/java/com/gitblit/wicket/pages/BlamePage.java index e45bbbc..2fcca0a 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlamePage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlamePage.java @@ -32,6 +32,7 @@ import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; @@ -93,9 +94,34 @@ final BlameType activeBlameType = BlameType.get(blameTypeParam); RevCommit commit = getCommit(); - - add(new BookmarkablePageLink<Void>("blobLink", BlobPage.class, - WicketUtils.newPathParameter(repositoryName, objectId, blobPath))); + + PathModel pathModel = null; + + List<PathModel> paths = JGitUtils.getFilesInPath(getRepository(), StringUtils.getRootPath(blobPath), commit); + for (PathModel path : paths) { + if (path.path.equals(blobPath)) { + pathModel = path; + break; + } + } + + if (pathModel == null) { + final String notFound = MessageFormat.format("Blame page failed to find {0} in {1} @ {2}", + blobPath, repositoryName, objectId); + logger.error(notFound); + add(new Label("annotation").setVisible(false)); + add(new Label("missingBlob", missingBlob(blobPath, commit)).setEscapeModelStrings(false)); + return; + } + + if (pathModel.isFilestoreItem()) { + String rawUrl = JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, pathModel.getFilestoreOid()); + add(new ExternalLink("blobLink", rawUrl)); + } else { + add(new BookmarkablePageLink<Void>("blobLink", BlobPage.class, + WicketUtils.newPathParameter(repositoryName, objectId, blobPath))); + } + add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, objectId))); add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class, @@ -134,23 +160,9 @@ final DateFormat df = new SimpleDateFormat(format); df.setTimeZone(getTimeZone()); - PathModel pathModel = null; - List<PathModel> paths = JGitUtils.getFilesInPath(getRepository(), StringUtils.getRootPath(blobPath), commit); - for (PathModel path : paths) { - if (path.path.equals(blobPath)) { - pathModel = path; - break; - } - } + - if (pathModel == null) { - final String notFound = MessageFormat.format("Blame page failed to find {0} in {1} @ {2}", - blobPath, repositoryName, objectId); - logger.error(notFound); - add(new Label("annotation").setVisible(false)); - add(new Label("missingBlob", missingBlob(blobPath, commit)).setEscapeModelStrings(false)); - return; - } + add(new Label("missingBlob").setVisible(false)); diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html index 2e0d57c..254d7d0 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html +++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html @@ -45,6 +45,7 @@ <td class="changeType"><span wicket:id="changeType">[change type]</span></td> <td class="path"><span wicket:id="pathName">[commit path]</span></td> <td class="hidden-phone rightAlign"> + <span wicket:id="filestore" style="margin-right:20px;" class="aui-lozenge aui-lozenge-moved"></span> <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span> <span class="link"> <span class="hidden-tablet"><a wicket:id="patch"><wicket:message key="gb.patch"></wicket:message></a> | </span><a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a><span class="hidden-tablet"> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a></span> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java index b56d721..9bc1570 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java @@ -135,6 +135,8 @@ @Override public void populateItem(final Item<PathChangeModel> item) { final PathChangeModel entry = item.getModelObject(); + final String filestoreItemUrl = entry.isFilestoreItem() ? JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, entry.getFilestoreOid()) : null; + Label changeType = new Label("changeType", ""); WicketUtils.setChangeTypeCssClass(changeType, entry.changeType); setChangeTypeTooltip(changeType, entry.changeType); @@ -143,6 +145,7 @@ boolean hasSubmodule = false; String submodulePath = null; + if (entry.isTree()) { // tree item.add(new LinkPanel("pathName", null, entry.path, TreePage.class, @@ -159,11 +162,13 @@ item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId)); } else { // add relative link - item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId)); + item.add(new LinkPanel("pathName", "list", entry.path, entry.isFilestoreItem() ? filestoreItemUrl : "#n" + entry.objectId)); } // quick links if (entry.isSubmodule()) { + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); + item.add(new ExternalLink("raw", "").setEnabled(false)); // submodule item.add(new ExternalLink("patch", "").setEnabled(false)); @@ -179,12 +184,24 @@ .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.ADD) && !entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path)) - .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path); - item.add(new ExternalLink("raw", rawUrl) - .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); + + if (entry.isFilestoreItem()) { + item.add(new Label("filestore", getString("gb.filestore")).setVisible(true)); + + item.add(new ExternalLink("view", filestoreItemUrl)); + item.add(new ExternalLink("raw", filestoreItemUrl)); + } else { + + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); + + item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils + .newPathParameter(repositoryName, entry.commitId, entry.path)) + .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); + + item.add(new ExternalLink("raw", RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path)) + .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); + } + item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.ADD) diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.html b/src/main/java/com/gitblit/wicket/pages/CommitPage.html index 2aa10f2..23e9438 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitPage.html +++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.html @@ -77,8 +77,9 @@ <table class="pretty"> <tr wicket:id="changedPath"> <td class="changeType"><span wicket:id="changeType">[change type]</span></td> - <td class="path"><span wicket:id="pathName">[commit path]</span></td> + <td class="path"><span wicket:id="pathName">[commit path]</span></td> <td class="hidden-phone rightAlign"> + <span wicket:id="filestore" style="margin-right:20px;" class="aui-lozenge aui-lozenge-moved"></span> <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span> <span class="link"> <a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <span class="hidden-tablet"><a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | </span><a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.java b/src/main/java/com/gitblit/wicket/pages/CommitPage.java index 0a1a68d..c841173 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.java @@ -163,6 +163,8 @@ @Override public void populateItem(final Item<PathChangeModel> item) { final PathChangeModel entry = item.getModelObject(); + final String filestoreItemUrl = entry.isFilestoreItem() ? JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, entry.getFilestoreOid()) : null; + Label changeType = new Label("changeType", ""); WicketUtils.setChangeTypeCssClass(changeType, entry.changeType); setChangeTypeTooltip(changeType, entry.changeType); @@ -194,9 +196,13 @@ path = JGitUtils.getStringContent(getRepository(), getCommit().getTree(), path); displayPath = entry.path + " -> " + path; } - item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class, - WicketUtils - .newPathParameter(repositoryName, entry.commitId, path))); + + if (entry.isFilestoreItem()) { + item.add(new LinkPanel("pathName", "list", entry.path, filestoreItemUrl)); + } else { + item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class, + WicketUtils.newPathParameter(repositoryName, entry.commitId, path))); + } } @@ -204,6 +210,8 @@ if (entry.isSubmodule()) { item.add(new ExternalLink("raw", "").setEnabled(false)); + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); + // submodule item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) @@ -220,12 +228,22 @@ .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.ADD) && !entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path)) - .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path); - item.add(new ExternalLink("raw", rawUrl) - .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); + + if (entry.isFilestoreItem()) { + item.add(new Label("filestore", getString("gb.filestore")).setVisible(true)); + + item.add(new ExternalLink("view", filestoreItemUrl)); + item.add(new ExternalLink("raw", filestoreItemUrl)); + } else { + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); + + item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils + .newPathParameter(repositoryName, entry.commitId, entry.path)) + .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); + String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path); + item.add(new ExternalLink("raw", rawUrl) + .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); + } item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.ADD) diff --git a/src/main/java/com/gitblit/wicket/pages/FilestorePage.java b/src/main/java/com/gitblit/wicket/pages/FilestorePage.java index be0181b..7c3bb9d 100644 --- a/src/main/java/com/gitblit/wicket/pages/FilestorePage.java +++ b/src/main/java/com/gitblit/wicket/pages/FilestorePage.java @@ -35,7 +35,6 @@ import com.gitblit.wicket.CacheControl; import com.gitblit.wicket.FilestoreUI; import com.gitblit.wicket.GitBlitWebSession; -import com.gitblit.wicket.RequiresAdminRole; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.CacheControl.LastModified; diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.html b/src/main/java/com/gitblit/wicket/pages/TreePage.html index 51a996f..c07f9c5 100644 --- a/src/main/java/com/gitblit/wicket/pages/TreePage.html +++ b/src/main/java/com/gitblit/wicket/pages/TreePage.html @@ -22,7 +22,8 @@ <table style="width:100%" class="pretty"> <tr wicket:id="changedPath"> <td class="hidden-phone icon"><img wicket:id="pathIcon" /></td> - <td><span wicket:id="pathName"></span></td> + <td><span wicket:id="pathName"></span></td> + <td class="hidden-phone filestore"><span wicket:id="filestore" class="aui-lozenge aui-lozenge-moved"></span></td> <td class="hidden-phone size"><span wicket:id="pathSize">[path size]</span></td> <td class="hidden-phone mode"><span wicket:id="pathPermissions">[path permissions]</span></td> <td class="treeLinks"><span wicket:id="pathLinks">[path links]</span></td> diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java index d7899dc..f138214 100644 --- a/src/main/java/com/gitblit/wicket/pages/TreePage.java +++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java @@ -70,12 +70,13 @@ if (path.lastIndexOf('/') > -1) { parentPath = path.substring(0, path.lastIndexOf('/')); } - PathModel model = new PathModel("..", parentPath, 0, FileMode.TREE.getBits(), null, objectId); + PathModel model = new PathModel("..", parentPath, null, 0, FileMode.TREE.getBits(), null, objectId); model.isParentPath = true; paths.add(0, model); } final String id = getBestCommitId(commit); + final ByteFormat byteFormat = new ByteFormat(); final String baseUrl = WicketUtils.getGitblitURL(getRequest()); @@ -88,7 +89,9 @@ @Override public void populateItem(final Item<PathModel> item) { PathModel entry = item.getModelObject(); + item.add(new Label("pathPermissions", JGitUtils.getPermissionsFromMode(entry.mode))); + if (entry.isParentPath) { // parent .. path item.add(WicketUtils.newBlankImage("pathIcon")); @@ -96,6 +99,7 @@ item.add(new LinkPanel("pathName", null, entry.name, TreePage.class, WicketUtils .newPathParameter(repositoryName, id, entry.path))); + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); item.add(new Label("pathLinks", "")); } else { if (entry.isTree()) { @@ -105,6 +109,8 @@ item.add(new LinkPanel("pathName", "list", entry.name, TreePage.class, WicketUtils.newPathParameter(repositoryName, id, entry.path))); + + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); // links Fragment links = new Fragment("pathLinks", "treeLinks", this); @@ -133,6 +139,8 @@ getShortObjectId(submoduleId), TreePage.class, WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule)); + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); + Fragment links = new Fragment("pathLinks", "submoduleLinks", this); links.add(new BookmarkablePageLink<Void>("view", SummaryPage.class, WicketUtils.newRepositoryParameter(submodulePath)).setEnabled(hasSubmodule)); @@ -155,17 +163,33 @@ } item.add(WicketUtils.getFileImage("pathIcon", entry.name)); item.add(new Label("pathSize", byteFormat.format(entry.size))); - item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class, - WicketUtils.newPathParameter(repositoryName, id, - path))); - + // links Fragment links = new Fragment("pathLinks", "blobLinks", this); - links.add(new BookmarkablePageLink<Void>("view", BlobPage.class, - WicketUtils.newPathParameter(repositoryName, id, - path))); - String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, path); - links.add(new ExternalLink("raw", rawUrl)); + + if (entry.isFilestoreItem()) { + item.add(new Label("filestore", getString("gb.filestore")).setVisible(true)); + + final String filestoreItemUrl = JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, entry.getFilestoreOid()); + + item.add(new LinkPanel("pathName", "list", displayPath, filestoreItemUrl)); + links.add(new ExternalLink("view", filestoreItemUrl)); + links.add(new ExternalLink("raw", filestoreItemUrl)); + + } else { + item.add(new Label("filestore", getString("gb.filestore")).setVisible(false)); + + item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class, + WicketUtils.newPathParameter(repositoryName, id, + path))); + + links.add(new BookmarkablePageLink<Void>("view", BlobPage.class, + WicketUtils.newPathParameter(repositoryName, id, + path))); + String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, path); + links.add(new ExternalLink("raw", rawUrl)); + } + links.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils.newPathParameter(repositoryName, id, path))); diff --git a/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java b/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java index a3f127b..75fd70e 100644 --- a/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java @@ -109,7 +109,7 @@ tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path))); while (tw.next()) { if (tw.getPathString().equals(path)) { - matchingPath = new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw + matchingPath = new PathChangeModel(tw.getPathString(), tw.getPathString(), null, 0, tw .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(), ChangeType.MODIFY); } diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css index 0cc8fd0..0d36da7 100644 --- a/src/main/resources/gitblit.css +++ b/src/main/resources/gitblit.css @@ -1944,6 +1944,12 @@ padding-right:15px; } +td.filestore { + text-align: right; + width:1em; + padding-right:15px; +} + td.size { text-align: right; width: 8em; diff --git a/src/test/java/com/gitblit/tests/JGitUtilsTest.java b/src/test/java/com/gitblit/tests/JGitUtilsTest.java index 2cf4a5a..c273e86 100644 --- a/src/test/java/com/gitblit/tests/JGitUtilsTest.java +++ b/src/test/java/com/gitblit/tests/JGitUtilsTest.java @@ -585,16 +585,16 @@ @Test public void testZip() throws Exception { - assertFalse(CompressionUtils.zip(null, null, null, null)); + assertFalse(CompressionUtils.zip(null, null, null, null, null)); Repository repository = GitBlitSuite.getHelloworldRepository(); File zipFileA = new File(GitBlitSuite.REPOSITORIES, "helloworld.zip"); FileOutputStream fosA = new FileOutputStream(zipFileA); - boolean successA = CompressionUtils.zip(repository, null, Constants.HEAD, fosA); + boolean successA = CompressionUtils.zip(repository, null, null, Constants.HEAD, fosA); fosA.close(); File zipFileB = new File(GitBlitSuite.REPOSITORIES, "helloworld-java.zip"); FileOutputStream fosB = new FileOutputStream(zipFileB); - boolean successB = CompressionUtils.zip(repository, "java.java", Constants.HEAD, fosB); + boolean successB = CompressionUtils.zip(repository, null, "java.java", Constants.HEAD, fosB); fosB.close(); repository.close(); -- Gitblit v1.9.1