Skip to content

Commit 9197d34

Browse files
committed
Download zip feature.
1 parent 8a2e9c3 commit 9197d34

File tree

12 files changed

+214
-9
lines changed

12 files changed

+214
-9
lines changed

distrib/gitblit.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ web.siteName =
5555
# If web.authenticate=false, any user can execute the aforementioned functions.
5656
web.allowAdministration = true
5757

58+
# Allow dyanamic zip downloads.
59+
web.allowZipDownloads = true
60+
5861
# This is the message display above the repositories table.
5962
# This can point to a file with Markdown content.
6063
# Specifying "gitblit" uses the internal welcome message.

src/com/gitblit/Constants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public class Constants {
1717
public final static String ADMIN_ROLE = "#admin";
1818

1919
public final static String PROPERTIES_FILE = "gitblit.properties";
20+
21+
public final static String GIT_SERVLET_PATH = "/git/";
22+
23+
public final static String ZIP_SERVLET_PATH = "/zip/";
2024

2125
public static enum AccessRestrictionType {
2226
NONE, PUSH, CLONE, VIEW;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.gitblit;
2+
3+
import java.util.Date;
4+
5+
import javax.servlet.http.HttpServlet;
6+
import javax.servlet.http.HttpServletResponse;
7+
8+
import org.eclipse.jgit.lib.Repository;
9+
import org.eclipse.jgit.revwalk.RevCommit;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import com.gitblit.Constants.AccessRestrictionType;
14+
import com.gitblit.utils.JGitUtils;
15+
import com.gitblit.utils.StringUtils;
16+
import com.gitblit.wicket.models.RepositoryModel;
17+
18+
public class DownloadZipServlet extends HttpServlet {
19+
20+
public static String asLink(String baseURL, String repository, String objectId, String path) {
21+
return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository + (path == null ? "" : ("&p=" + path)) + (objectId == null ? "" : ("&h=" + objectId));
22+
}
23+
24+
private static final long serialVersionUID = 1L;
25+
26+
private final static Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
27+
28+
public DownloadZipServlet() {
29+
super();
30+
}
31+
32+
@Override
33+
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
34+
processRequest(request, response);
35+
}
36+
37+
@Override
38+
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
39+
processRequest(request, response);
40+
}
41+
42+
private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
43+
if (!GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)) {
44+
logger.warn("Zip downloads are disabled");
45+
response.sendError(HttpServletResponse.SC_FORBIDDEN);
46+
return;
47+
48+
}
49+
String repository = request.getParameter("r");
50+
String basePath = request.getParameter("p");
51+
String objectId = request.getParameter("h");
52+
53+
try {
54+
String name = repository;
55+
if (name.indexOf('/') > -1) {
56+
name = name.substring(name.lastIndexOf('/') + 1);
57+
}
58+
59+
// check roles first
60+
boolean authorized = request.isUserInRole(Constants.ADMIN_ROLE);
61+
authorized |= request.isUserInRole(repository);
62+
63+
if (!authorized) {
64+
RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
65+
if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {
66+
logger.warn("Unauthorized access via zip servlet for " + model.name);
67+
response.sendError(HttpServletResponse.SC_FORBIDDEN);
68+
return;
69+
}
70+
}
71+
if (!StringUtils.isEmpty(basePath)) {
72+
name += "-" + basePath.replace('/', '_');
73+
}
74+
if (!StringUtils.isEmpty(objectId)) {
75+
name += "-" + objectId;
76+
}
77+
78+
Repository r = GitBlit.self().getRepository(repository);
79+
RevCommit commit = JGitUtils.getCommit(r, objectId);
80+
Date date = JGitUtils.getCommitDate(commit);
81+
String contentType = "application/octet-stream";
82+
response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());
83+
// response.setContentLength(attachment.getFileSize());
84+
response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" + "\"");
85+
response.setDateHeader("Last-Modified", date.getTime());
86+
response.setHeader("Cache-Control", "no-cache");
87+
response.setHeader("Pragma", "no-cache");
88+
response.setDateHeader("Expires", 0);
89+
90+
try {
91+
JGitUtils.zip(r, basePath, objectId, response.getOutputStream());
92+
response.flushBuffer();
93+
} catch (Throwable t) {
94+
logger.error("Failed to write attachment to client", t);
95+
}
96+
} catch (Throwable t) {
97+
logger.error("Failed to write attachment to client", t);
98+
}
99+
}
100+
}

src/com/gitblit/GitBlitServer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,13 @@ private static void start(Params params) {
211211
wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);
212212
wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");
213213
rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);
214-
214+
215+
// Zip Servlet
216+
rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");
217+
215218
// Git Servlet
216219
ServletHolder gitServlet = null;
217-
String gitServletPathSpec = "/git/*";
220+
String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*";
218221
if (fileSettings.getBoolean(Keys.git.enableGitServlet, true)) {
219222
gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);
220223
gitServlet.setInitParameter("base-path", params.repositoriesFolder);

src/com/gitblit/tests/JGitUtilsTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.gitblit.tests;
22

33
import java.io.File;
4+
import java.io.FileOutputStream;
45
import java.util.Date;
56
import java.util.List;
67

@@ -103,5 +104,27 @@ public void testCommitDiff() throws Exception {
103104
r.close();
104105
System.out.println(diff);
105106
}
107+
108+
public void testZip() throws Exception {
109+
Repository r = new FileRepository(new File(repositoriesFolder, "gitblit.git/" + Constants.DOT_GIT));
110+
FileOutputStream fos = null;
111+
try {
112+
File zipFile = new File("c:/output.zip");
113+
zipFile.delete();
114+
fos = new FileOutputStream(zipFile);
115+
if (JGitUtils.zip(r, "src", Constants.HEAD, fos)) {
116+
System.out.println("zip = " + zipFile.length() + " bytes");
117+
} else {
118+
System.err.println("failed to generate zip file?!");
119+
}
120+
} finally {
121+
if (fos != null) {
122+
try {
123+
fos.close();
124+
} catch (Throwable t) {
125+
}
126+
}
127+
}
128+
}
106129

107130
}

src/com/gitblit/utils/JGitUtils.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.io.File;
55
import java.io.IOException;
66
import java.io.InputStream;
7+
import java.io.OutputStream;
78
import java.nio.charset.Charset;
89
import java.text.DateFormat;
910
import java.text.ParseException;
@@ -17,6 +18,8 @@
1718
import java.util.Map;
1819
import java.util.Set;
1920
import java.util.concurrent.atomic.AtomicInteger;
21+
import java.util.zip.ZipEntry;
22+
import java.util.zip.ZipOutputStream;
2023

2124
import org.eclipse.jgit.api.Git;
2225
import org.eclipse.jgit.diff.DiffEntry;
@@ -808,10 +811,57 @@ public static StoredConfig readConfig(Repository r) {
808811
return null;
809812
}
810813

814+
public static boolean zip(Repository r, String basePath, String objectId, OutputStream os) throws Exception {
815+
RevCommit commit = getCommit(r, objectId);
816+
if (commit == null) {
817+
return false;
818+
}
819+
final RevWalk rw = new RevWalk(r);
820+
final TreeWalk walk = new TreeWalk(r);
821+
try {
822+
walk.addTree(commit.getTree());
823+
ZipOutputStream zos = new ZipOutputStream(os);
824+
zos.setComment("Generated by Git:Blit");
825+
if (basePath != null && basePath.length() > 0) {
826+
PathFilter f = PathFilter.create(basePath);
827+
walk.setFilter(f);
828+
}
829+
walk.setRecursive(true);
830+
while (walk.next()) {
831+
ZipEntry entry = new ZipEntry(walk.getPathString());
832+
entry.setSize(walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB));
833+
entry.setComment(commit.getName());
834+
zos.putNextEntry(entry);
835+
836+
ObjectId entid = walk.getObjectId(0);
837+
FileMode entmode = walk.getFileMode(0);
838+
RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType());
839+
rw.parseBody(blob);
840+
841+
ObjectLoader ldr = r.open(blob.getId(), Constants.OBJ_BLOB);
842+
byte[] tmp = new byte[4096];
843+
InputStream in = ldr.openStream();
844+
int n;
845+
while ((n = in.read(tmp)) > 0) {
846+
zos.write(tmp, 0, n);
847+
}
848+
in.close();
849+
}
850+
zos.finish();
851+
return true;
852+
} catch (IOException e) {
853+
LOGGER.error("Failed to zip files from commit " + commit.getName(), e);
854+
} finally {
855+
walk.release();
856+
rw.dispose();
857+
}
858+
return false;
859+
}
860+
811861
public static List<Metric> getDateMetrics(Repository r) {
812862
Metric total = new Metric("TOTAL");
813863
final Map<String, Metric> metricMap = new HashMap<String, Metric>();
814-
864+
815865
if (hasCommits(r)) {
816866
final List<RefModel> tags = getTags(r, -1);
817867
final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();

src/com/gitblit/wicket/GitBlitWebApp.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,5 @@ gb.showRemoteBranchesDescription = show remote branches
9292
gb.canAdminDescription = can administer Git:Blit server
9393
gb.permittedUsers = permitted users
9494
gb.isFrozen = is frozen
95-
gb.isFrozenDescription = deny push operations
95+
gb.isFrozenDescription = deny push operations
96+
gb.zip = zip

src/com/gitblit/wicket/pages/CommitPage.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
<tr><th><wicket:message key="gb.committer">committer</wicket:message></th><td><span class="sha1" wicket:id="commitCommitter">[committer]</span></td></tr>
2424
<tr><th></th><td><span class="sha1" wicket:id="commitCommitterDate">[commit date]</span></td></tr>
2525
<tr><th><wicket:message key="gb.commit">commit</wicket:message></th><td><span class="sha1" wicket:id="commitId">[commit id]</span></td></tr>
26-
<tr><th><wicket:message key="gb.tree">tree</wicket:message></th><td><span class="sha1" wicket:id="commitTree">[commit tree]</span></td></tr>
26+
<tr><th><wicket:message key="gb.tree">tree</wicket:message></th>
27+
<td><span class="sha1" wicket:id="commitTree">[commit tree]</span>
28+
<span class="link">
29+
<a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>
30+
</span>
31+
</td></tr>
2732
<tr><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th>
2833
<td>
2934
<span wicket:id="commitParents">

src/com/gitblit/wicket/pages/CommitPage.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
import org.apache.wicket.PageParameters;
77
import org.apache.wicket.markup.html.basic.Label;
88
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
9+
import org.apache.wicket.markup.html.link.ExternalLink;
910
import org.apache.wicket.markup.repeater.Item;
1011
import org.apache.wicket.markup.repeater.data.DataView;
1112
import org.apache.wicket.markup.repeater.data.ListDataProvider;
1213
import org.apache.wicket.model.StringResourceModel;
1314
import org.eclipse.jgit.lib.Repository;
1415
import org.eclipse.jgit.revwalk.RevCommit;
1516

17+
import com.gitblit.DownloadZipServlet;
18+
import com.gitblit.GitBlit;
19+
import com.gitblit.Keys;
1620
import com.gitblit.utils.JGitUtils;
1721
import com.gitblit.utils.JGitUtils.SearchType;
1822
import com.gitblit.wicket.LinkPanel;
@@ -62,6 +66,8 @@ public CommitPage(PageParameters params) {
6266
add(new Label("commitId", c.getName()));
6367

6468
add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class, newCommitParameter()));
69+
add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
70+
add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, null)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
6571

6672
// Parent Commits
6773
ListDataProvider<String> parentsDp = new ListDataProvider<String>(parents);

src/com/gitblit/wicket/pages/TreePage.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<!-- blob nav links -->
1111
<div class="page_nav2">
12-
<a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>
12+
<a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>
1313
</div>
1414

1515
<!-- commit header -->
@@ -32,7 +32,7 @@
3232
<!-- tree links -->
3333
<wicket:fragment wicket:id="treeLinks">
3434
<span class="link">
35-
<a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
35+
<a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a>
3636
</span>
3737
</wicket:fragment>
3838

0 commit comments

Comments
 (0)