]> source.dussan.org Git - gitblit.git/commitdiff
Fix for #976 - Filestore links via browser 988/head
authorPaul Martin <paul@paulsputer.com>
Fri, 25 Dec 2015 22:35:11 +0000 (22:35 +0000)
committerPaul Martin <paul@paulsputer.com>
Fri, 25 Dec 2015 22:35:11 +0000 (22:35 +0000)
+ GitLFS client support
+ FilestoreModel now parses meta file
+ Read meta heading from cache if available
+ Authentication based on accept headers for browser view filestore login
+ PathModel & PathChangeModel now understands filestore items
+ Zip & Rar downloads contain include filestore items
+ Filestore servlet returns LFS JSON error only if accepted by client
+ DiffStat now knows repository to allow identification of filestore items
+ Filestore items identified and returned via view, raw & blob links on
blame, commitDiff, commit and Tree pages

24 files changed:
src/main/java/com/gitblit/Constants.java
src/main/java/com/gitblit/models/FilestoreModel.java
src/main/java/com/gitblit/models/PathModel.java
src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java
src/main/java/com/gitblit/servlet/DownloadZipServlet.java
src/main/java/com/gitblit/servlet/FilestoreServlet.java
src/main/java/com/gitblit/servlet/GitFilter.java
src/main/java/com/gitblit/servlet/RawServlet.java
src/main/java/com/gitblit/utils/CompressionUtils.java
src/main/java/com/gitblit/utils/DiffStatFormatter.java
src/main/java/com/gitblit/utils/DiffUtils.java
src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java
src/main/java/com/gitblit/utils/JGitUtils.java
src/main/java/com/gitblit/wicket/pages/BlamePage.java
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
src/main/java/com/gitblit/wicket/pages/CommitPage.html
src/main/java/com/gitblit/wicket/pages/CommitPage.java
src/main/java/com/gitblit/wicket/pages/FilestorePage.java
src/main/java/com/gitblit/wicket/pages/TreePage.html
src/main/java/com/gitblit/wicket/pages/TreePage.java
src/main/java/com/gitblit/wicket/panels/HistoryPanel.java
src/main/resources/gitblit.css
src/test/java/com/gitblit/tests/JGitUtilsTest.java

index 0a99953a626810a78a7a172c9a67ab743c8fa12c..6232552e189895c124fd2d0969a49f6de7f5bf7d 100644 (file)
@@ -94,6 +94,10 @@ public class Constants {
        public static final int LEN_SHORTLOG = 78;\r
 \r
        public static final int LEN_SHORTLOG_REFS = 60;\r
+       \r
+       public static final int LEN_FILESTORE_META_MIN = 125;\r
+       \r
+       public static final int LEN_FILESTORE_META_MAX = 146;\r
 \r
        public static final String DEFAULT_BRANCH = "default";\r
 \r
index 4144df6b2403beff1f39302af948f68d420826c9..2ed1ede90830a18572e6bce2284884346aed8d94 100644 (file)
@@ -20,6 +20,10 @@ import java.util.ArrayList;
 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 @@ public class FilestoreModel implements Serializable {
 
        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 @@ public class FilestoreModel implements Serializable {
        //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;
@@ -53,6 +75,29 @@ public class FilestoreModel implements Serializable {
                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() {
                return size;
        }
@@ -102,19 +147,25 @@ public class FilestoreModel implements Serializable {
        }
        
        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 class FilestoreModel implements Serializable {
        
        public static enum Status {
 
+               ReferenceOnly(-42),
+               
                Deleted(-30),
                AuthenticationRequired(-20),
                
index bf585425d6562559f343a4d4c3862b28517277c7..3c280eb457ff3b92b4e297def2712b99480bf0ab 100644 (file)
  */\r
 package com.gitblit.models;\r
 \r
+import java.io.IOException;\r
 import java.io.Serializable;\r
 \r
 import org.eclipse.jgit.diff.DiffEntry;\r
 import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;\r
+import org.eclipse.jgit.errors.MissingObjectException;\r
+import org.eclipse.jgit.lib.Constants;\r
 import org.eclipse.jgit.lib.FileMode;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevWalk;\r
+\r
+import com.gitblit.manager.FilestoreManager;\r
+import com.gitblit.utils.JGitUtils;\r
 \r
 /**\r
  * PathModel is a serializable model class that represents a file or a folder,\r
@@ -34,16 +43,18 @@ public class PathModel implements Serializable, Comparable<PathModel> {
 \r
        public final String name;\r
        public final String path;\r
+       private final FilestoreModel filestoreItem;\r
        public final long size;\r
        public final int mode;\r
        public final String objectId;\r
        public final String commitId;\r
        public boolean isParentPath;\r
-\r
-       public PathModel(String name, String path, long size, int mode, String objectId, String commitId) {\r
+       \r
+       public PathModel(String name, String path, FilestoreModel filestoreItem, long size, int mode, String objectId, String commitId) {\r
                this.name = name;\r
                this.path = path;\r
-               this.size = size;\r
+               this.filestoreItem = filestoreItem;\r
+               this.size = (filestoreItem == null) ? size : filestoreItem.getSize();\r
                this.mode = mode;\r
                this.objectId = objectId;\r
                this.commitId = commitId;\r
@@ -66,6 +77,18 @@ public class PathModel implements Serializable, Comparable<PathModel> {
                                || FileMode.EXECUTABLE_FILE.equals(mode)\r
                                || (FileMode.MISSING.equals(mode) && !isSymlink() && !isSubmodule() && !isTree());\r
        }\r
+       \r
+       public boolean isFilestoreItem() {\r
+               return filestoreItem != null;\r
+       }\r
+       \r
+       public String getFilestoreOid() {\r
+               if (filestoreItem != null) {\r
+                       return filestoreItem.oid;\r
+               }\r
+               \r
+               return null;\r
+       }\r
 \r
        @Override\r
        public int hashCode() {\r
@@ -119,9 +142,9 @@ public class PathModel implements Serializable, Comparable<PathModel> {
 \r
                public int deletions;\r
 \r
-               public PathChangeModel(String name, String path, long size, int mode, String objectId,\r
+               public PathChangeModel(String name, String path, FilestoreModel filestoreItem, long size, int mode, String objectId,\r
                                String commitId, ChangeType type) {\r
-                       super(name, path, size, mode, objectId, commitId);\r
+                       super(name, path, filestoreItem, size, mode, objectId, commitId);\r
                        this.changeType = type;\r
                }\r
 \r
@@ -148,18 +171,33 @@ public class PathModel implements Serializable, Comparable<PathModel> {
                        return super.equals(o);\r
                }\r
 \r
-               public static PathChangeModel from(DiffEntry diff, String commitId) {\r
+               public static PathChangeModel from(DiffEntry diff, String commitId, Repository repository) {\r
                        PathChangeModel pcm;\r
+                       FilestoreModel filestoreItem = null;\r
+                       long size = 0;\r
+\r
+                       if (repository != null) {\r
+                               try (RevWalk revWalk = new RevWalk(repository)) {\r
+                                       size = revWalk.getObjectReader().getObjectSize(diff.getNewId().toObjectId(), Constants.OBJ_BLOB);\r
+       \r
+                                       if (JGitUtils.isPossibleFilestoreItem(size)) {\r
+                                               filestoreItem = JGitUtils.getFilestoreItem(revWalk.getObjectReader().open(diff.getNewId().toObjectId()));\r
+                                       }\r
+                               } catch (Exception e) {\r
+                                               e.printStackTrace();\r
+                               }\r
+                       }\r
+                       \r
                        if (diff.getChangeType().equals(ChangeType.DELETE)) {\r
-                               pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff\r
+                               pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), filestoreItem, size, diff\r
                                                .getNewMode().getBits(), diff.getOldId().name(), commitId, diff\r
                                                .getChangeType());\r
                        } else if (diff.getChangeType().equals(ChangeType.RENAME)) {\r
-                               pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff\r
+                               pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), filestoreItem, size, diff\r
                                                .getNewMode().getBits(), diff.getNewId().name(), commitId, diff\r
                                                .getChangeType());\r
                        } else {\r
-                               pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff\r
+                               pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), filestoreItem, size, diff\r
                                                .getNewMode().getBits(), diff.getNewId().name(), commitId, diff\r
                                                .getChangeType());\r
                        }\r
index b9cb088545e6ca35124c39b53568dfd362ff7ba2..e1d76db0722c6cf7f3ca5b9fde87ca630aa75bc0 100644 (file)
@@ -133,10 +133,11 @@ public abstract class AccessRestrictionFilter extends AuthenticationFilter {
        /**\r
         * Allows authentication header to be altered based on the action requested\r
         * Default is WWW-Authenticate\r
+        * @param httpRequest\r
         * @param action\r
         * @return authentication type header\r
         */\r
-       protected String getAuthenticationHeader(String action) {\r
+       protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) {\r
                return "WWW-Authenticate";\r
        }\r
        \r
@@ -192,7 +193,7 @@ public abstract class AccessRestrictionFilter extends AuthenticationFilter {
                                                logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl));\r
                                        }\r
                                        \r
-                                       httpResponse.setHeader(getAuthenticationHeader(urlRequestType), CHALLENGE);\r
+                                       httpResponse.setHeader(getAuthenticationHeader(httpRequest, urlRequestType), CHALLENGE);\r
                                        httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
                                        return;\r
                                } else {\r
@@ -239,7 +240,7 @@ public abstract class AccessRestrictionFilter extends AuthenticationFilter {
                                if (runtimeManager.isDebugMode()) {\r
                                        logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));\r
                                }\r
-                               httpResponse.setHeader(getAuthenticationHeader(urlRequestType), CHALLENGE);\r
+                               httpResponse.setHeader(getAuthenticationHeader(httpRequest, urlRequestType), CHALLENGE);\r
                                httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
                                return;\r
                        } else {\r
index 07562561efa9ae5c7b6ae6f4b8842fc26dcd5ad8..319c4f91f8c48a528aca25bf1f012b3d1706c654 100644 (file)
@@ -22,6 +22,7 @@ import java.util.Date;
 \r
 import com.google.inject.Inject;\r
 import com.google.inject.Singleton;\r
+\r
 import javax.servlet.ServletException;\r
 import javax.servlet.http.HttpServlet;\r
 import javax.servlet.http.HttpServletResponse;\r
@@ -34,6 +35,7 @@ import org.slf4j.LoggerFactory;
 import com.gitblit.Constants;\r
 import com.gitblit.IStoredSettings;\r
 import com.gitblit.Keys;\r
+import com.gitblit.manager.IFilestoreManager;\r
 import com.gitblit.manager.IRepositoryManager;\r
 import com.gitblit.utils.CompressionUtils;\r
 import com.gitblit.utils.JGitUtils;\r
@@ -57,6 +59,8 @@ public class DownloadZipServlet extends HttpServlet {
        private IStoredSettings settings;\r
 \r
        private IRepositoryManager repositoryManager;\r
+       \r
+       private IFilestoreManager filestoreManager;\r
 \r
        public static enum Format {\r
                zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");\r
@@ -78,9 +82,10 @@ public class DownloadZipServlet extends HttpServlet {
        }\r
 \r
        @Inject\r
-       public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager) {\r
+       public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager, IFilestoreManager filestoreManager) {\r
                this.settings = settings;\r
                this.repositoryManager = repositoryManager;\r
+               this.filestoreManager = filestoreManager;\r
        }\r
 \r
        /**\r
@@ -169,22 +174,23 @@ public class DownloadZipServlet extends HttpServlet {
                        response.setHeader("Pragma", "no-cache");\r
                        response.setDateHeader("Expires", 0);\r
 \r
+                       \r
                        try {\r
                                switch (format) {\r
                                case zip:\r
-                                       CompressionUtils.zip(r, basePath, objectId, response.getOutputStream());\r
+                                       CompressionUtils.zip(r, filestoreManager, basePath, objectId, response.getOutputStream());\r
                                        break;\r
                                case tar:\r
-                                       CompressionUtils.tar(r, basePath, objectId, response.getOutputStream());\r
+                                       CompressionUtils.tar(r, filestoreManager, basePath, objectId, response.getOutputStream());\r
                                        break;\r
                                case gz:\r
-                                       CompressionUtils.gz(r, basePath, objectId, response.getOutputStream());\r
+                                       CompressionUtils.gz(r, filestoreManager, basePath, objectId, response.getOutputStream());\r
                                        break;\r
                                case xz:\r
-                                       CompressionUtils.xz(r, basePath, objectId, response.getOutputStream());\r
+                                       CompressionUtils.xz(r, filestoreManager, basePath, objectId, response.getOutputStream());\r
                                        break;\r
                                case bzip2:\r
-                                       CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());\r
+                                       CompressionUtils.bzip2(r, filestoreManager, basePath, objectId, response.getOutputStream());\r
                                        break;\r
                                }\r
 \r
index 8f303fcfcdc4853f9672ce400c9f0d10f0ece74e..4af9084d53ce6181c3421b90813bc15526c02617 100644 (file)
@@ -238,7 +238,10 @@ public class FilestoreServlet extends HttpServlet {
                        }
                } else {
                        response.setStatus(responseObject.error.code);
-                       serialize(response, responseObject.error);
+                       
+                       if (isMetaRequest) {
+                               serialize(response, responseObject.error);
+                       }
                }
        };
        
index 27408f02393e8f109f48bb67e0686047b7aa1e75..9522893ea1ec868faface17aa5894585f77ecf83 100644 (file)
@@ -102,8 +102,8 @@ public class GitFilter extends AccessRestrictionFilter {
        }\r
 \r
        /**\r
-        * Analyze the url and returns the action of the request. Return values are\r
-        * either "/git-receive-pack" or "/git-upload-pack".\r
+        * Analyze the url and returns the action of the request. Return values are:\r
+        * "/git-receive-pack", "/git-upload-pack" or "/info/lfs".\r
         *\r
         * @param serverUrl\r
         * @return action of the request\r
@@ -316,18 +316,22 @@ public class GitFilter extends AccessRestrictionFilter {
        \r
        /**\r
         * Git lfs action uses an alternative authentication header, \r
+        * dependent on the viewing method.\r
         * \r
+        * @param httpRequest\r
         * @param action\r
         * @return\r
         */\r
        @Override\r
-       protected String getAuthenticationHeader(String action) {\r
+       protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) {\r
 \r
                if (action.equals(gitLfs)) {\r
-                       return "LFS-Authenticate";\r
+                       if (hasContentInRequestHeader(httpRequest, "Accept", FilestoreServlet.GIT_LFS_META_MIME)) {\r
+                               return "LFS-Authenticate";\r
+                       }\r
                }\r
                \r
-               return super.getAuthenticationHeader(action);\r
+               return super.getAuthenticationHeader(httpRequest, action);\r
        }\r
        \r
        /**\r
index 897047d86066c352b67831f92ec7a41b2a94a087..dca57730246a188119a1e35773b605a35fab60e4 100644 (file)
@@ -364,7 +364,7 @@ public class RawServlet extends HttpServlet {
                                        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));
                                        }
                                }
 
index b06edd22e79c75f2089de46bb45a06e9861e1d31..1b8e6fc3dbb082916de7f651608f862fd78c34f2 100644 (file)
@@ -16,6 +16,9 @@
 package com.gitblit.utils;\r
 \r
 import java.io.ByteArrayOutputStream;\r
+import java.io.EOFException;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
 import java.io.IOException;\r
 import java.io.OutputStream;\r
 import java.text.MessageFormat;\r
@@ -28,9 +31,11 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;\r
 import org.apache.commons.compress.compressors.CompressorException;\r
 import org.apache.commons.compress.compressors.CompressorStreamFactory;\r
+import org.apache.commons.io.IOUtils;\r
 import org.eclipse.jgit.lib.Constants;\r
 import org.eclipse.jgit.lib.FileMode;\r
 import org.eclipse.jgit.lib.MutableObjectId;\r
+import org.eclipse.jgit.lib.ObjectId;\r
 import org.eclipse.jgit.lib.ObjectLoader;\r
 import org.eclipse.jgit.lib.ObjectReader;\r
 import org.eclipse.jgit.lib.Repository;\r
@@ -41,6 +46,11 @@ import org.eclipse.jgit.treewalk.filter.PathFilter;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.GitBlit;\r
+import com.gitblit.manager.IFilestoreManager;\r
+import com.gitblit.models.FilestoreModel;\r
+import com.gitblit.models.FilestoreModel.Status;\r
+\r
 /**\r
  * Collection of static methods for retrieving information from a repository.\r
  *\r
@@ -87,7 +97,7 @@ public class CompressionUtils {
         * @return true if repository was successfully zipped to supplied output\r
         *         stream\r
         */\r
-       public static boolean zip(Repository repository, String basePath, String objectId,\r
+       public static boolean zip(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId,\r
                        OutputStream os) {\r
                RevCommit commit = JGitUtils.getCommit(repository, objectId);\r
                if (commit == null) {\r
@@ -115,16 +125,40 @@ public class CompressionUtils {
                                        continue;\r
                                }\r
                                tw.getObjectId(id, 0);\r
-\r
+                               \r
+                               ObjectLoader loader = repository.open(id);\r
+                               \r
                                ZipArchiveEntry entry = new ZipArchiveEntry(tw.getPathString());\r
-                               entry.setSize(reader.getObjectSize(id, Constants.OBJ_BLOB));\r
+\r
+                               FilestoreModel filestoreItem = null;\r
+                               \r
+                               if (JGitUtils.isPossibleFilestoreItem(loader.getSize())) {\r
+                                       filestoreItem = JGitUtils.getFilestoreItem(tw.getObjectReader().open(id));\r
+                               }\r
+\r
+                               final long size = (filestoreItem == null) ? loader.getSize() : filestoreItem.getSize();  \r
+\r
+                               entry.setSize(size);\r
                                entry.setComment(commit.getName());\r
                                entry.setUnixMode(mode.getBits());\r
                                entry.setTime(modified);\r
                                zos.putArchiveEntry(entry);\r
+                               \r
+                               if (filestoreItem == null) {\r
+                                       //Copy repository stored file\r
+                                       loader.copyTo(zos);\r
+                               } else {\r
+                                       //Copy filestore file\r
+                                       try (FileInputStream streamIn = new FileInputStream(filestoreManager.getStoragePath(filestoreItem.oid))) {\r
+                                               IOUtils.copyLarge(streamIn, zos);\r
+                                       } catch (Throwable e) {\r
+                                               LOGGER.error(MessageFormat.format("Failed to archive filestore item {0}", filestoreItem.oid), e);\r
+\r
+                                               //Handle as per other errors \r
+                                               throw e; \r
+                                       }\r
+                               }\r
 \r
-                               ObjectLoader ldr = repository.open(id);\r
-                               ldr.copyTo(zos);\r
                                zos.closeArchiveEntry();\r
                        }\r
                        zos.finish();\r
@@ -151,9 +185,9 @@ public class CompressionUtils {
         * @return true if repository was successfully zipped to supplied output\r
         *         stream\r
         */\r
-       public static boolean tar(Repository repository, String basePath, String objectId,\r
+       public static boolean tar(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId,\r
                        OutputStream os) {\r
-               return tar(null, repository, basePath, objectId, os);\r
+               return tar(null, repository, filestoreManager, basePath, objectId, os);\r
        }\r
 \r
        /**\r
@@ -169,9 +203,9 @@ public class CompressionUtils {
         * @return true if repository was successfully zipped to supplied output\r
         *         stream\r
         */\r
-       public static boolean gz(Repository repository, String basePath, String objectId,\r
+       public static boolean gz(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId,\r
                        OutputStream os) {\r
-               return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os);\r
+               return tar(CompressorStreamFactory.GZIP, repository, filestoreManager, basePath, objectId, os);\r
        }\r
 \r
        /**\r
@@ -187,9 +221,9 @@ public class CompressionUtils {
         * @return true if repository was successfully zipped to supplied output\r
         *         stream\r
         */\r
-       public static boolean xz(Repository repository, String basePath, String objectId,\r
+       public static boolean xz(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId,\r
                        OutputStream os) {\r
-               return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os);\r
+               return tar(CompressorStreamFactory.XZ, repository, filestoreManager, basePath, objectId, os);\r
        }\r
 \r
        /**\r
@@ -205,10 +239,10 @@ public class CompressionUtils {
         * @return true if repository was successfully zipped to supplied output\r
         *         stream\r
         */\r
-       public static boolean bzip2(Repository repository, String basePath, String objectId,\r
+       public static boolean bzip2(Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId,\r
                        OutputStream os) {\r
 \r
-               return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os);\r
+               return tar(CompressorStreamFactory.BZIP2, repository, filestoreManager, basePath, objectId, os);\r
        }\r
 \r
        /**\r
@@ -227,7 +261,7 @@ public class CompressionUtils {
         * @return true if repository was successfully zipped to supplied output\r
         *         stream\r
         */\r
-       private static boolean tar(String algorithm, Repository repository, String basePath, String objectId,\r
+       private static boolean tar(String algorithm, Repository repository, IFilestoreManager filestoreManager, String basePath, String objectId,\r
                        OutputStream os) {\r
                RevCommit commit = JGitUtils.getCommit(repository, objectId);\r
                if (commit == null) {\r
@@ -263,6 +297,7 @@ public class CompressionUtils {
                                if (mode == FileMode.GITLINK || mode == FileMode.TREE) {\r
                                        continue;\r
                                }\r
+                               \r
                                tw.getObjectId(id, 0);\r
 \r
                                ObjectLoader loader = repository.open(id);\r
@@ -278,9 +313,34 @@ public class CompressionUtils {
                                        TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString());\r
                                        entry.setMode(mode.getBits());\r
                                        entry.setModTime(modified);\r
-                                       entry.setSize(loader.getSize());\r
+\r
+                                       FilestoreModel filestoreItem = null;\r
+                                       \r
+                                       if (JGitUtils.isPossibleFilestoreItem(loader.getSize())) {\r
+                                               filestoreItem = JGitUtils.getFilestoreItem(tw.getObjectReader().open(id));\r
+                                       }\r
+\r
+                                       final long size = (filestoreItem == null) ? loader.getSize() : filestoreItem.getSize();  \r
+\r
+                                       entry.setSize(size);\r
                                        tos.putArchiveEntry(entry);\r
-                                       loader.copyTo(tos);\r
+                                       \r
+                                       if (filestoreItem == null) {\r
+                                               //Copy repository stored file\r
+                                               loader.copyTo(tos);\r
+                                       } else {\r
+                                               //Copy filestore file\r
+                                               try (FileInputStream streamIn = new FileInputStream(filestoreManager.getStoragePath(filestoreItem.oid))) {\r
+\r
+                                                       IOUtils.copyLarge(streamIn, tos);\r
+                                               } catch (Throwable e) {\r
+                                                       LOGGER.error(MessageFormat.format("Failed to archive filestore item {0}", filestoreItem.oid), e);\r
+\r
+                                                       //Handle as per other errors \r
+                                                       throw e; \r
+                                               }\r
+                                       }\r
+                                       \r
                                        tos.closeArchiveEntry();\r
                                }\r
                        }\r
index 572046ee63560e64ec9e246951237c8ad05f9a9a..a6c3ae29466cf16cf93e0b7e5672dc97fac88155 100644 (file)
@@ -20,6 +20,7 @@ import java.io.IOException;
 import org.eclipse.jgit.diff.DiffEntry;\r
 import org.eclipse.jgit.diff.DiffFormatter;\r
 import org.eclipse.jgit.diff.RawText;\r
+import org.eclipse.jgit.lib.Repository;\r
 import org.eclipse.jgit.util.io.NullOutputStream;\r
 \r
 import com.gitblit.models.PathModel.PathChangeModel;\r
@@ -37,9 +38,9 @@ public class DiffStatFormatter extends DiffFormatter {
 \r
        private PathChangeModel path;\r
 \r
-       public DiffStatFormatter(String commitId) {\r
+       public DiffStatFormatter(String commitId, Repository repository) {\r
                super(NullOutputStream.INSTANCE);\r
-               diffStat = new DiffStat(commitId);\r
+               diffStat = new DiffStat(commitId, repository);\r
        }\r
 \r
        @Override\r
index cdebec1b53dfed8fcf158df14fc716953bb69048..41aab4cf7d2dfd03e055d116e531eb931dd47866 100644 (file)
@@ -157,13 +157,16 @@ public class DiffUtils {
                public final List<PathChangeModel> paths = new ArrayList<PathChangeModel>();\r
 \r
                private final String commitId;\r
+               \r
+               private final Repository repository;\r
 \r
-               public DiffStat(String commitId) {\r
+               public DiffStat(String commitId, Repository repository) {\r
                        this.commitId = commitId;\r
+                       this.repository = repository;\r
                }\r
 \r
                public PathChangeModel addPath(DiffEntry entry) {\r
-                       PathChangeModel pcm = PathChangeModel.from(entry, commitId);\r
+                       PathChangeModel pcm = PathChangeModel.from(entry, commitId, repository);\r
                        paths.add(pcm);\r
                        return pcm;\r
                }\r
@@ -379,7 +382,7 @@ public class DiffUtils {
                        DiffFormatter df;\r
                        switch (outputType) {\r
                        case HTML:\r
-                               df = new GitBlitDiffFormatter(commit.getName(), path, handler, tabLength);\r
+                               df = new GitBlitDiffFormatter(commit.getName(), repository, path, handler, tabLength);\r
                                break;\r
                        case PLAIN:\r
                        default:\r
@@ -548,7 +551,7 @@ public class DiffUtils {
                DiffStat stat = null;\r
                try {\r
                        RawTextComparator cmp = RawTextComparator.DEFAULT;\r
-                       DiffStatFormatter df = new DiffStatFormatter(commit.getName());\r
+                       DiffStatFormatter df = new DiffStatFormatter(commit.getName(), repository);\r
                        df.setRepository(repository);\r
                        df.setDiffComparator(cmp);\r
                        df.setDetectRenames(true);\r
index 86b7ca2eda3fa744e8bde7f314643e0e161a6ca3..8ebadbf175056b477bb18bb71f2a40bdbe3b8fec 100644 (file)
@@ -33,6 +33,7 @@ import org.eclipse.jgit.diff.DiffEntry;
 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 class GitBlitDiffFormatter extends DiffFormatter {
 
        }
 
-       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.
index c3d020738fb6978bf7c50b59d0c297bf4748556a..7a008139064fdb52a1c761772941175a06d865bc 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Iterator;
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.Map.Entry;\r
+import java.util.regex.Matcher;\r
 import java.util.regex.Pattern;\r
 \r
 import org.apache.commons.io.filefilter.TrueFileFilter;\r
@@ -42,6 +43,7 @@ import org.eclipse.jgit.diff.DiffFormatter;
 import org.eclipse.jgit.diff.RawTextComparator;\r
 import org.eclipse.jgit.errors.ConfigInvalidException;\r
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;\r
+import org.eclipse.jgit.errors.LargeObjectException;\r
 import org.eclipse.jgit.errors.MissingObjectException;\r
 import org.eclipse.jgit.errors.StopWalkException;\r
 import org.eclipse.jgit.lib.BlobBasedConfig;\r
@@ -84,12 +86,16 @@ import org.eclipse.jgit.util.FS;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
+import com.gitblit.GitBlit;\r
 import com.gitblit.GitBlitException;\r
+import com.gitblit.manager.GitblitManager;\r
+import com.gitblit.models.FilestoreModel;\r
 import com.gitblit.models.GitNote;\r
 import com.gitblit.models.PathModel;\r
 import com.gitblit.models.PathModel.PathChangeModel;\r
 import com.gitblit.models.RefModel;\r
 import com.gitblit.models.SubmoduleModel;\r
+import com.gitblit.servlet.FilestoreServlet;\r
 import com.google.common.base.Strings;\r
 \r
 /**\r
@@ -993,23 +999,40 @@ public class JGitUtils {
                                tw.setRecursive(true);\r
                                tw.addTree(commit.getTree());\r
                                while (tw.next()) {\r
-                                       list.add(new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw\r
-                                                       .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(),\r
+                                       long size = 0;\r
+                                       FilestoreModel filestoreItem = null;\r
+                                       ObjectId objectId = tw.getObjectId(0);\r
+                                       \r
+                                       try {\r
+                                               if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {\r
+\r
+                                                       size = tw.getObjectReader().getObjectSize(objectId, Constants.OBJ_BLOB);\r
+\r
+                                                       if (isPossibleFilestoreItem(size)) {\r
+                                                               filestoreItem = getFilestoreItem(tw.getObjectReader().open(objectId));\r
+                                                       }\r
+                                               }\r
+                                       } catch (Throwable t) {\r
+                                               error(t, null, "failed to retrieve blob size for " + tw.getPathString());\r
+                                       }\r
+                                       \r
+                                       list.add(new PathChangeModel(tw.getPathString(), tw.getPathString(),filestoreItem, size, tw\r
+                                                       .getRawMode(0), objectId.getName(), commit.getId().getName(),\r
                                                        ChangeType.ADD));\r
                                }\r
                                tw.close();\r
                        } else {\r
                                RevCommit parent = rw.parseCommit(commit.getParent(0).getId());\r
-                               DiffStatFormatter df = new DiffStatFormatter(commit.getName());\r
+                               DiffStatFormatter df = new DiffStatFormatter(commit.getName(), repository);\r
                                df.setRepository(repository);\r
                                df.setDiffComparator(RawTextComparator.DEFAULT);\r
                                df.setDetectRenames(true);\r
                                List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree());\r
                                for (DiffEntry diff : diffs) {\r
                                        // create the path change model\r
-                                       PathChangeModel pcm = PathChangeModel.from(diff, commit.getName());\r
-\r
-                                       if (calculateDiffStat) {\r
+                                       PathChangeModel pcm = PathChangeModel.from(diff, commit.getName(), repository);\r
+                                               \r
+                                               if (calculateDiffStat) {\r
                                                // update file diffstats\r
                                                df.format(diff);\r
                                                PathChangeModel pathStat = df.getDiffStat().getPath(pcm.path);\r
@@ -1083,7 +1106,7 @@ public class JGitUtils {
 \r
                        List<DiffEntry> diffEntries = df.scan(startCommit.getTree(), endCommit.getTree());\r
                        for (DiffEntry diff : diffEntries) {\r
-                               PathChangeModel pcm = PathChangeModel.from(diff,  endCommit.getName());\r
+                               PathChangeModel pcm = PathChangeModel.from(diff,  endCommit.getName(), repository);\r
                                list.add(pcm);\r
                        }\r
                        Collections.sort(list);\r
@@ -1167,22 +1190,55 @@ public class JGitUtils {
        private static PathModel getPathModel(TreeWalk tw, String basePath, RevCommit commit) {\r
                String name;\r
                long size = 0;\r
+               \r
                if (StringUtils.isEmpty(basePath)) {\r
                        name = tw.getPathString();\r
                } else {\r
                        name = tw.getPathString().substring(basePath.length() + 1);\r
                }\r
                ObjectId objectId = tw.getObjectId(0);\r
+               FilestoreModel filestoreItem = null;\r
+               \r
                try {\r
                        if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {\r
+\r
                                size = tw.getObjectReader().getObjectSize(objectId, Constants.OBJ_BLOB);\r
+\r
+                               if (isPossibleFilestoreItem(size)) {\r
+                                       filestoreItem = getFilestoreItem(tw.getObjectReader().open(objectId));\r
+                               }\r
                        }\r
                } catch (Throwable t) {\r
                        error(t, null, "failed to retrieve blob size for " + tw.getPathString());\r
                }\r
-               return new PathModel(name, tw.getPathString(), size, tw.getFileMode(0).getBits(),\r
+               return new PathModel(name, tw.getPathString(), filestoreItem, size, tw.getFileMode(0).getBits(),\r
                                objectId.getName(), commit.getName());\r
        }\r
+       \r
+       public static boolean isPossibleFilestoreItem(long size) {\r
+               return (   (size >= com.gitblit.Constants.LEN_FILESTORE_META_MIN) \r
+                               && (size <= com.gitblit.Constants.LEN_FILESTORE_META_MAX));\r
+       }\r
+       \r
+       /**\r
+        * \r
+        * @return Representative FilestoreModel if valid, otherwise null\r
+        */\r
+       public static FilestoreModel getFilestoreItem(ObjectLoader obj){\r
+               try {\r
+                       final byte[] blob = obj.getCachedBytes(com.gitblit.Constants.LEN_FILESTORE_META_MAX);\r
+                       final String meta = new String(blob, "UTF-8");\r
+               \r
+                       return FilestoreModel.fromMetaString(meta);\r
+\r
+               } catch (LargeObjectException e) {\r
+                       //Intentionally failing silent\r
+               } catch (Exception e) {\r
+                       error(e, null, "failed to retrieve filestoreItem " + obj.toString());\r
+               }\r
+               \r
+               return null;\r
+       }\r
 \r
        /**\r
         * Returns a path model by path string\r
@@ -1197,29 +1253,34 @@ public class JGitUtils {
                        throws IOException {\r
 \r
                long size = 0;\r
+               FilestoreModel filestoreItem = null;\r
                TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());\r
                String pathString = path;\r
 \r
-                       if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {\r
-                               size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB);\r
-                               pathString = PathUtils.getLastPathComponent(pathString);\r
-\r
-                       } else if (tw.isSubtree()) {\r
+               if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {\r
 \r
-                               // do not display dirs that are behind in the path\r
-                               if (!Strings.isNullOrEmpty(filter)) {\r
-                                       pathString = path.replaceFirst(filter + "/", "");\r
-                               }\r
+                       pathString = PathUtils.getLastPathComponent(pathString);\r
+                       \r
+                       size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB);\r
+                       \r
+                       if (isPossibleFilestoreItem(size)) {\r
+                               filestoreItem = getFilestoreItem(tw.getObjectReader().open(tw.getObjectId(0)));\r
+                       }\r
+               } else if (tw.isSubtree()) {\r
 \r
-                               // remove the last slash from path in displayed link\r
-                               if (pathString != null && pathString.charAt(pathString.length()-1) == '/') {\r
-                                       pathString = pathString.substring(0, pathString.length()-1);\r
-                               }\r
+                       // do not display dirs that are behind in the path\r
+                       if (!Strings.isNullOrEmpty(filter)) {\r
+                               pathString = path.replaceFirst(filter + "/", "");\r
                        }\r
 \r
-                       return new PathModel(pathString, tw.getPathString(), size, tw.getFileMode(0).getBits(),\r
-                                       tw.getObjectId(0).getName(), commit.getName());\r
+                       // remove the last slash from path in displayed link\r
+                       if (pathString != null && pathString.charAt(pathString.length()-1) == '/') {\r
+                               pathString = pathString.substring(0, pathString.length()-1);\r
+                       }\r
+               }\r
 \r
+               return new PathModel(pathString, tw.getPathString(), filestoreItem, size, tw.getFileMode(0).getBits(),\r
+                               tw.getObjectId(0).getName(), commit.getName());\r
 \r
        }\r
 \r
@@ -2513,4 +2574,27 @@ public class JGitUtils {
                }\r
                return new MergeResult(MergeStatus.FAILED, null);\r
        }\r
+       \r
+       \r
+       /**\r
+        * Returns the LFS URL for the given oid \r
+        * Currently assumes that the Gitblit Filestore is used \r
+        *\r
+        * @param baseURL\r
+        * @param repository name\r
+        * @param oid of lfs item\r
+        * @return the lfs item URL\r
+        */\r
+       public static String getLfsRepositoryUrl(String baseURL, String repositoryName, String oid) {\r
+               \r
+               if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
+                       baseURL = baseURL.substring(0, baseURL.length() - 1);\r
+               }\r
+               \r
+               return baseURL + com.gitblit.Constants.R_PATH \r
+                                          + repositoryName + "/" \r
+                                          + com.gitblit.Constants.R_LFS \r
+                                          + "objects/" + oid;\r
+               \r
+       }\r
 }\r
index e45bbbc816ff9f8eda99f58daa2c9376479d6fb6..2fcca0ae15cf416484abba3432750a240da03fd3 100644 (file)
@@ -32,6 +32,7 @@ import org.apache.wicket.PageParameters;
 import org.apache.wicket.behavior.SimpleAttributeModifier;\r
 import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
 import org.apache.wicket.markup.repeater.Item;\r
 import org.apache.wicket.markup.repeater.data.DataView;\r
 import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
@@ -93,9 +94,34 @@ public class BlamePage extends RepositoryPage {
                final BlameType activeBlameType = BlameType.get(blameTypeParam);\r
 \r
                RevCommit commit = getCommit();\r
-\r
-               add(new BookmarkablePageLink<Void>("blobLink", BlobPage.class,\r
-                               WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));\r
+               \r
+               PathModel pathModel = null;\r
+               \r
+               List<PathModel> paths = JGitUtils.getFilesInPath(getRepository(), StringUtils.getRootPath(blobPath), commit);\r
+               for (PathModel path : paths) {\r
+                       if (path.path.equals(blobPath)) {\r
+                               pathModel = path;\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               if (pathModel == null) {\r
+                       final String notFound = MessageFormat.format("Blame page failed to find {0} in {1} @ {2}",\r
+                                       blobPath, repositoryName, objectId);\r
+                       logger.error(notFound);\r
+                       add(new Label("annotation").setVisible(false));\r
+                       add(new Label("missingBlob", missingBlob(blobPath, commit)).setEscapeModelStrings(false));\r
+                       return;\r
+               }\r
+               \r
+               if (pathModel.isFilestoreItem()) {\r
+                       String rawUrl = JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, pathModel.getFilestoreOid());\r
+                       add(new ExternalLink("blobLink", rawUrl));\r
+               } else {\r
+                       add(new BookmarkablePageLink<Void>("blobLink", BlobPage.class,\r
+                                       WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));     \r
+               }\r
+               \r
                add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,\r
                                WicketUtils.newObjectParameter(repositoryName, objectId)));\r
                add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class,\r
@@ -134,23 +160,9 @@ public class BlamePage extends RepositoryPage {
                final DateFormat df = new SimpleDateFormat(format);\r
                df.setTimeZone(getTimeZone());\r
 \r
-               PathModel pathModel = null;\r
-               List<PathModel> paths = JGitUtils.getFilesInPath(getRepository(), StringUtils.getRootPath(blobPath), commit);\r
-               for (PathModel path : paths) {\r
-                       if (path.path.equals(blobPath)) {\r
-                               pathModel = path;\r
-                               break;\r
-                       }\r
-               }\r
+               \r
 \r
-               if (pathModel == null) {\r
-                       final String notFound = MessageFormat.format("Blame page failed to find {0} in {1} @ {2}",\r
-                                       blobPath, repositoryName, objectId);\r
-                       logger.error(notFound);\r
-                       add(new Label("annotation").setVisible(false));\r
-                       add(new Label("missingBlob", missingBlob(blobPath, commit)).setEscapeModelStrings(false));\r
-                       return;\r
-               }\r
+               \r
 \r
                add(new Label("missingBlob").setVisible(false));\r
 \r
index 2e0d57ce9f40dc63473901f388056156a70fdd12..254d7d0c4d4d9a6c00951864478a540ebf0b4ebd 100644 (file)
@@ -45,6 +45,7 @@
                        <td class="changeType"><span wicket:id="changeType">[change type]</span></td>           \r
                        <td class="path"><span wicket:id="pathName">[commit path]</span></td>                   \r
                        <td class="hidden-phone rightAlign">\r
+                               <span wicket:id="filestore" style="margin-right:20px;" class="aui-lozenge aui-lozenge-moved"></span>\r
                                <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span>\r
                                <span class="link">\r
                                        <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>\r
index b56d721c64209c9ce53b6d8c42f20e572a42642c..9bc1570d44b02a4b8dc546c25ef412f0504d3a9c 100644 (file)
@@ -135,6 +135,8 @@ public class CommitDiffPage extends RepositoryPage {
                        @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 @@ public class CommitDiffPage extends RepositoryPage {
 
                                boolean hasSubmodule = false;
                                String submodulePath = null;
+                               
                                if (entry.isTree()) {
                                        // tree
                                        item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
@@ -159,11 +162,13 @@ public class CommitDiffPage extends RepositoryPage {
                                        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 @@ public class CommitDiffPage extends RepositoryPage {
                                                        .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)
index 2aa10f26d527855f0961e90acae48c94c5228e16..23e94380ce9ea930b6dcaa4de5d9638f49c091a7 100644 (file)
@@ -77,8 +77,9 @@
        <table class="pretty">\r
                <tr wicket:id="changedPath">\r
                        <td class="changeType"><span wicket:id="changeType">[change type]</span></td>\r
-                       <td class="path"><span wicket:id="pathName">[commit path]</span></td>                   \r
+                       <td class="path"><span wicket:id="pathName">[commit path]</span></td>   \r
                        <td class="hidden-phone rightAlign">\r
+                               <span wicket:id="filestore" style="margin-right:20px;" class="aui-lozenge aui-lozenge-moved"></span>\r
                                <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span>\r
                                <span class="link">\r
                                        <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>\r
index 0a1a68d46ac69f09c458511ee92221c4b70fefb2..c8411738675f9ef4c40a44eed4c420c58e5a2a31 100644 (file)
@@ -163,6 +163,8 @@ public class CommitPage extends RepositoryPage {
                        @Override\r
                        public void populateItem(final Item<PathChangeModel> item) {\r
                                final PathChangeModel entry = item.getModelObject();\r
+                               final String filestoreItemUrl = entry.isFilestoreItem() ? JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, entry.getFilestoreOid()) : null;\r
+                               \r
                                Label changeType = new Label("changeType", "");\r
                                WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);\r
                                setChangeTypeTooltip(changeType, entry.changeType);\r
@@ -194,9 +196,13 @@ public class CommitPage extends RepositoryPage {
                                                path = JGitUtils.getStringContent(getRepository(), getCommit().getTree(), path);\r
                                                displayPath = entry.path + " -> " + path;\r
                                        }\r
-                                       item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class,\r
-                                                       WicketUtils\r
-                                                                       .newPathParameter(repositoryName, entry.commitId, path)));\r
+                                       \r
+                                       if (entry.isFilestoreItem()) {\r
+                                               item.add(new LinkPanel("pathName", "list", entry.path, filestoreItemUrl));\r
+                                       } else {\r
+                                               item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class,\r
+                                                       WicketUtils.newPathParameter(repositoryName, entry.commitId, path)));\r
+                                       }\r
                                }\r
 \r
 \r
@@ -204,6 +210,8 @@ public class CommitPage extends RepositoryPage {
                                if (entry.isSubmodule()) {\r
                                        item.add(new ExternalLink("raw", "").setEnabled(false));\r
 \r
+                                       item.add(new Label("filestore", getString("gb.filestore")).setVisible(false));\r
+                                       \r
                                        // submodule\r
                                        item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils\r
                                                        .newPathParameter(repositoryName, entry.commitId, entry.path))\r
@@ -220,12 +228,22 @@ public class CommitPage extends RepositoryPage {
                                                        .newPathParameter(repositoryName, entry.commitId, entry.path))\r
                                                        .setEnabled(!entry.changeType.equals(ChangeType.ADD)\r
                                                                        && !entry.changeType.equals(ChangeType.DELETE)));\r
-                                       item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils\r
-                                                       .newPathParameter(repositoryName, entry.commitId, entry.path))\r
-                                                       .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));\r
-                                       String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path);\r
-                                       item.add(new ExternalLink("raw", rawUrl)\r
-                                                       .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));\r
+                                       \r
+                                       if (entry.isFilestoreItem()) {\r
+                                               item.add(new Label("filestore", getString("gb.filestore")).setVisible(true));\r
+                                               \r
+                                               item.add(new ExternalLink("view", filestoreItemUrl));\r
+                                               item.add(new ExternalLink("raw", filestoreItemUrl));\r
+                                       } else {\r
+                                               item.add(new Label("filestore", getString("gb.filestore")).setVisible(false));\r
+                                               \r
+                                               item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils\r
+                                                               .newPathParameter(repositoryName, entry.commitId, entry.path))\r
+                                                               .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));\r
+                                               String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path);\r
+                                               item.add(new ExternalLink("raw", rawUrl)\r
+                                                               .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));\r
+                                       }\r
                                        item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils\r
                                                        .newPathParameter(repositoryName, entry.commitId, entry.path))\r
                                                        .setEnabled(!entry.changeType.equals(ChangeType.ADD)\r
index be0181bf109f84e979e389f3936e748d2e988411..7c3bb9d31aa615f9e5331ec1184aad49acca79b4 100644 (file)
@@ -35,7 +35,6 @@ import com.gitblit.models.UserModel;
 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;
 
index 51a996fff33124ea546d949424448b44605ef142..c07f9c508f42af1776fcc7f3060e26318705edf1 100644 (file)
@@ -22,7 +22,8 @@
        <table style="width:100%" class="pretty">\r
                <tr wicket:id="changedPath">\r
                        <td class="hidden-phone icon"><img wicket:id="pathIcon" /></td>\r
-                       <td><span wicket:id="pathName"></span></td>                     \r
+                       <td><span wicket:id="pathName"></span></td>\r
+                       <td class="hidden-phone filestore"><span wicket:id="filestore" class="aui-lozenge aui-lozenge-moved"></span></td>\r
                        <td class="hidden-phone size"><span wicket:id="pathSize">[path size]</span></td>\r
                        <td class="hidden-phone mode"><span wicket:id="pathPermissions">[path permissions]</span></td>\r
                        <td class="treeLinks"><span wicket:id="pathLinks">[path links]</span></td>\r
index d7899dcb057ca63fbe05cc47af371908137d9181..f13821472703580854ad462482ebc26133690e45 100644 (file)
@@ -70,12 +70,13 @@ public class TreePage extends RepositoryPage {
                        if (path.lastIndexOf('/') > -1) {\r
                                parentPath = path.substring(0, path.lastIndexOf('/'));\r
                        }\r
-                       PathModel model = new PathModel("..", parentPath, 0, FileMode.TREE.getBits(), null, objectId);\r
+                       PathModel model = new PathModel("..", parentPath, null, 0, FileMode.TREE.getBits(), null, objectId);\r
                        model.isParentPath = true;\r
                        paths.add(0, model);\r
                }\r
 \r
                final String id = getBestCommitId(commit);\r
+               \r
                final ByteFormat byteFormat = new ByteFormat();\r
                final String baseUrl = WicketUtils.getGitblitURL(getRequest());\r
 \r
@@ -88,7 +89,9 @@ public class TreePage extends RepositoryPage {
                        @Override\r
                        public void populateItem(final Item<PathModel> item) {\r
                                PathModel entry = item.getModelObject();\r
+                               \r
                                item.add(new Label("pathPermissions", JGitUtils.getPermissionsFromMode(entry.mode)));\r
+                               \r
                                if (entry.isParentPath) {\r
                                        // parent .. path\r
                                        item.add(WicketUtils.newBlankImage("pathIcon"));\r
@@ -96,6 +99,7 @@ public class TreePage extends RepositoryPage {
                                        item.add(new LinkPanel("pathName", null, entry.name, TreePage.class,\r
                                                        WicketUtils\r
                                                                        .newPathParameter(repositoryName, id, entry.path)));\r
+                                       item.add(new Label("filestore", getString("gb.filestore")).setVisible(false));\r
                                        item.add(new Label("pathLinks", ""));\r
                                } else {\r
                                        if (entry.isTree()) {\r
@@ -106,6 +110,8 @@ public class TreePage extends RepositoryPage {
                                                                WicketUtils.newPathParameter(repositoryName, id,\r
                                                                                entry.path)));\r
 \r
+                                               item.add(new Label("filestore", getString("gb.filestore")).setVisible(false));\r
+\r
                                                // links\r
                                                Fragment links = new Fragment("pathLinks", "treeLinks", this);\r
                                                links.add(new BookmarkablePageLink<Void>("tree", TreePage.class,\r
@@ -133,6 +139,8 @@ public class TreePage extends RepositoryPage {
                                                                getShortObjectId(submoduleId), TreePage.class,\r
                                                                WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));\r
 \r
+                                               item.add(new Label("filestore", getString("gb.filestore")).setVisible(false));\r
+                                               \r
                                                Fragment links = new Fragment("pathLinks", "submoduleLinks", this);\r
                                                links.add(new BookmarkablePageLink<Void>("view", SummaryPage.class,\r
                                                                WicketUtils.newRepositoryParameter(submodulePath)).setEnabled(hasSubmodule));\r
@@ -155,17 +163,33 @@ public class TreePage extends RepositoryPage {
                                                }\r
                                                item.add(WicketUtils.getFileImage("pathIcon", entry.name));\r
                                                item.add(new Label("pathSize", byteFormat.format(entry.size)));\r
-                                               item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class,\r
-                                                               WicketUtils.newPathParameter(repositoryName, id,\r
-                                                                               path)));\r
-\r
+                                               \r
                                                // links\r
                                                Fragment links = new Fragment("pathLinks", "blobLinks", this);\r
-                                               links.add(new BookmarkablePageLink<Void>("view", BlobPage.class,\r
-                                                               WicketUtils.newPathParameter(repositoryName, id,\r
-                                                                               path)));\r
-                                               String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, path);\r
-                                               links.add(new ExternalLink("raw", rawUrl));\r
+                                               \r
+                                               if (entry.isFilestoreItem()) {\r
+                                                       item.add(new Label("filestore", getString("gb.filestore")).setVisible(true));\r
+                                                       \r
+                                                       final String filestoreItemUrl = JGitUtils.getLfsRepositoryUrl(getContextUrl(), repositoryName, entry.getFilestoreOid());\r
+                                                       \r
+                                                       item.add(new LinkPanel("pathName", "list", displayPath, filestoreItemUrl));\r
+                                                       links.add(new ExternalLink("view", filestoreItemUrl));\r
+                                                       links.add(new ExternalLink("raw", filestoreItemUrl));\r
+                                                       \r
+                                               } else {\r
+                                                       item.add(new Label("filestore", getString("gb.filestore")).setVisible(false));\r
+                                                       \r
+                                                       item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class,\r
+                                                                       WicketUtils.newPathParameter(repositoryName, id,\r
+                                                                                       path)));\r
+                                                       \r
+                                                       links.add(new BookmarkablePageLink<Void>("view", BlobPage.class,\r
+                                                                       WicketUtils.newPathParameter(repositoryName, id,\r
+                                                                                       path)));\r
+                                                       String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, path);\r
+                                                       links.add(new ExternalLink("raw", rawUrl));\r
+                                               }\r
+                                               \r
                                                links.add(new BookmarkablePageLink<Void>("blame", BlamePage.class,\r
                                                                WicketUtils.newPathParameter(repositoryName, id,\r
                                                                                path)));\r
index a3f127b1feb1896a67812382e1adef15787b2205..75fd70e76c6d0012d8ca5b496bfb1581c4f634b1 100644 (file)
@@ -109,7 +109,7 @@ public class HistoryPanel extends BasePanel {
                                        tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));\r
                                        while (tw.next()) {\r
                                                if (tw.getPathString().equals(path)) {\r
-                                                       matchingPath = new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw\r
+                                                       matchingPath = new PathChangeModel(tw.getPathString(), tw.getPathString(), null, 0, tw\r
                                                                .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(),\r
                                                                ChangeType.MODIFY);\r
                                                }\r
index 0cc8fd02d2fc79b45f41d1285a373f3f1f857bc8..0d36da7fc684990fb2609c435df5113575748118 100644 (file)
@@ -1944,6 +1944,12 @@ td.mode {
        padding-right:15px;\r
 }\r
 \r
+td.filestore {\r
+       text-align: right;\r
+       width:1em;\r
+       padding-right:15px;\r
+}\r
+\r
 td.size {\r
        text-align: right;\r
        width: 8em;     \r
index 2cf4a5a11d30546be6528152a423f78d80adffc2..c273e860c223142aeb072aa21a02f5df5a821b3f 100644 (file)
@@ -585,16 +585,16 @@ public class JGitUtilsTest extends GitblitUnitTest {
 \r
        @Test\r
        public void testZip() throws Exception {\r
-               assertFalse(CompressionUtils.zip(null, null, null, null));\r
+               assertFalse(CompressionUtils.zip(null, null, null, null, null));\r
                Repository repository = GitBlitSuite.getHelloworldRepository();\r
                File zipFileA = new File(GitBlitSuite.REPOSITORIES, "helloworld.zip");\r
                FileOutputStream fosA = new FileOutputStream(zipFileA);\r
-               boolean successA = CompressionUtils.zip(repository, null, Constants.HEAD, fosA);\r
+               boolean successA = CompressionUtils.zip(repository, null, null, Constants.HEAD, fosA);\r
                fosA.close();\r
 \r
                File zipFileB = new File(GitBlitSuite.REPOSITORIES, "helloworld-java.zip");\r
                FileOutputStream fosB = new FileOutputStream(zipFileB);\r
-               boolean successB = CompressionUtils.zip(repository, "java.java", Constants.HEAD, fosB);\r
+               boolean successB = CompressionUtils.zip(repository, null, "java.java", Constants.HEAD, fosB);\r
                fosB.close();\r
 \r
                repository.close();\r