diff --git a/src/main/scala/gitbucket/core/controller/ControllerBase.scala b/src/main/scala/gitbucket/core/controller/ControllerBase.scala
index eeedd1c..5db473f 100644
--- a/src/main/scala/gitbucket/core/controller/ControllerBase.scala
+++ b/src/main/scala/gitbucket/core/controller/ControllerBase.scala
@@ -57,7 +57,7 @@
// Redirect to dashboard
httpResponse.sendRedirect(baseUrl + "/")
}
- } else if(path.startsWith("/git/")){
+ } else if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
// Git repository
chain.doFilter(request, response)
} else {
diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
index e336a8e..8d07303 100644
--- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
+++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
@@ -315,9 +315,9 @@
}.toMap
response.setContentLength(attrs("size").toInt)
- val hash = attrs("oid").split(":")(1)
+ val oid = attrs("oid").split(":")(1)
- using(new FileInputStream(Directory.LfsHome + "/" + hash.substring(0, 2) + "/" + hash.substring(2, 4) + "/" + hash)){ in =>
+ using(new FileInputStream(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)){ in =>
IOUtils.copy(in, response.getOutputStream)
}
} else {
diff --git a/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala b/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala
new file mode 100644
index 0000000..afac430
--- /dev/null
+++ b/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala
@@ -0,0 +1,78 @@
+package gitbucket.core.servlet
+
+import java.io.{File, FileInputStream, FileOutputStream}
+import java.text.MessageFormat
+import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
+
+import gitbucket.core.util.Directory
+import org.apache.commons.io.{FileUtils, IOUtils}
+import org.json4s.jackson.Serialization._
+import org.apache.http.HttpStatus
+import gitbucket.core.util.ControlUtil._
+
+/**
+ * Provides GitLFS Transfer API
+ * https://github.com/git-lfs/git-lfs/blob/master/docs/api/basic-transfers.md
+ */
+class GitLfsTransferServlet extends HttpServlet {
+
+ private implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats
+ private val LongObjectIdLength = 32
+ private val LongObjectIdStringLength = LongObjectIdLength * 2
+
+ override protected def doGet(req: HttpServletRequest, res: HttpServletResponse): Unit = {
+ for {
+ oid <- getObjectId(req, res)
+ } yield {
+ val file = new File(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)
+ if(file.exists()){
+ res.setStatus(HttpStatus.SC_OK)
+ res.setContentType("application/octet-stream")
+ res.setContentLength(file.length.toInt)
+ using(new FileInputStream(file), res.getOutputStream){ (in, out) =>
+ IOUtils.copy(in, out)
+ out.flush()
+ }
+ } else {
+ sendError(res, HttpStatus.SC_NOT_FOUND,
+ MessageFormat.format("Object ''{0}'' not found", oid))
+ }
+ }
+ }
+
+ override protected def doPut(req: HttpServletRequest, res: HttpServletResponse): Unit = {
+ for {
+ oid <- getObjectId(req, res)
+ } yield {
+ val file = new File(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)
+ FileUtils.forceMkdir(file.getParentFile)
+ using(req.getInputStream, new FileOutputStream(file)){ (in, out) =>
+ IOUtils.copy(in, out)
+ }
+ res.setStatus(HttpStatus.SC_OK)
+ }
+ }
+
+ private def getObjectId(req: HttpServletRequest, rsp: HttpServletResponse): Option[String] = {
+ val info: String = req.getPathInfo
+ val length: Int = 1 + LongObjectIdStringLength
+ if (info.length != length) {
+ sendError(rsp, HttpStatus.SC_UNPROCESSABLE_ENTITY,
+ MessageFormat.format("Invalid pathInfo ''{0}'' does not match ''/'{'SHA-256'}'''", info))
+ None
+ } else {
+ Some(info.substring(1, length))
+ }
+ }
+
+ private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
+ res.setStatus(status)
+ using(res.getWriter()){ out =>
+ out.write(write(GitLfs.Error(message)))
+ out.flush()
+ }
+ }
+
+}
+
+
diff --git a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala
index 879ab5c..c0cf9db 100644
--- a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala
+++ b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala
@@ -61,6 +61,10 @@
}
}
+ /**
+ * Provides GitLFS Batch API
+ * https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
+ */
protected def serviceGitLfsBatchAPI(req: HttpServletRequest, res: HttpServletResponse): Unit = {
val batchRequest = read[GitLfs.BatchRequest](req.getInputStream)
val settings = loadSystemSettings()
@@ -96,10 +100,10 @@
}
res.setContentType("application/vnd.git-lfs+json")
-
- val out = res.getWriter
- out.print(write(batchResponse))
- out.flush()
+ using(res.getWriter){ out =>
+ out.print(write(batchResponse))
+ out.flush()
+ }
}
}
}
@@ -317,4 +321,8 @@
expires_at: Date
)
+ case class Error(
+ message: String
+ )
+
}
\ No newline at end of file
diff --git a/src/main/scala/gitbucket/core/util/ControlUtil.scala b/src/main/scala/gitbucket/core/util/ControlUtil.scala
index a323c42..74569ac 100644
--- a/src/main/scala/gitbucket/core/util/ControlUtil.scala
+++ b/src/main/scala/gitbucket/core/util/ControlUtil.scala
@@ -20,6 +20,20 @@
}
}
+ def using[A <% { def close(): Unit }, B <% { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
+ try f(resource1, resource2) finally {
+ if(resource1 != null){
+ ignoring(classOf[Throwable]) {
+ resource1.close()
+ }
+ }
+ if(resource2 != null){
+ ignoring(classOf[Throwable]) {
+ resource2.close()
+ }
+ }
+ }
+
def using[T](git: Git)(f: Git => T): T =
try f(git) finally git.getRepository.close()
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 9634066..36b904e 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -46,6 +46,17 @@
/git/*
+
+ GitLfsTransferServlet
+ gitbucket.core.servlet.GitLfsTransferServlet
+
+
+
+ GitLfsTransferServlet
+ /git-lfs/*
+
+
+