/*
|
* Copyright 2012 gitblit.com.
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
package com.gitblit.utils;
|
|
import java.io.IOException;
|
import java.io.OutputStream;
|
import java.text.MessageFormat;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
import org.apache.commons.compress.compressors.CompressorException;
|
import org.apache.commons.compress.compressors.CompressorStreamFactory;
|
import org.apache.wicket.util.io.ByteArrayOutputStream;
|
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.FileMode;
|
import org.eclipse.jgit.lib.MutableObjectId;
|
import org.eclipse.jgit.lib.ObjectLoader;
|
import org.eclipse.jgit.lib.ObjectReader;
|
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
|
/**
|
* Collection of static methods for retrieving information from a repository.
|
*
|
* @author James Moger
|
*
|
*/
|
public class CompressionUtils {
|
|
static final Logger LOGGER = LoggerFactory.getLogger(CompressionUtils.class);
|
|
/**
|
* Log an error message and exception.
|
*
|
* @param t
|
* @param repository
|
* if repository is not null it MUST be the {0} parameter in the
|
* pattern.
|
* @param pattern
|
* @param objects
|
*/
|
private static void error(Throwable t, Repository repository, String pattern, Object... objects) {
|
List<Object> parameters = new ArrayList<Object>();
|
if (objects != null && objects.length > 0) {
|
for (Object o : objects) {
|
parameters.add(o);
|
}
|
}
|
if (repository != null) {
|
parameters.add(0, repository.getDirectory().getAbsolutePath());
|
}
|
LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);
|
}
|
|
/**
|
* Zips the contents of the tree at the (optionally) specified revision and
|
* the (optionally) specified basepath to the supplied outputstream.
|
*
|
* @param repository
|
* @param basePath
|
* if unspecified, entire repository is assumed.
|
* @param objectId
|
* if unspecified, HEAD is assumed.
|
* @param os
|
* @return true if repository was successfully zipped to supplied output
|
* stream
|
*/
|
public static boolean zip(Repository repository, String basePath, String objectId,
|
OutputStream os) {
|
RevCommit commit = JGitUtils.getCommit(repository, objectId);
|
if (commit == null) {
|
return false;
|
}
|
boolean success = false;
|
RevWalk rw = new RevWalk(repository);
|
TreeWalk tw = new TreeWalk(repository);
|
try {
|
tw.reset();
|
tw.addTree(commit.getTree());
|
ZipArchiveOutputStream zos = new ZipArchiveOutputStream(os);
|
zos.setComment("Generated by Gitblit");
|
if (!StringUtils.isEmpty(basePath)) {
|
PathFilter f = PathFilter.create(basePath);
|
tw.setFilter(f);
|
}
|
tw.setRecursive(true);
|
MutableObjectId id = new MutableObjectId();
|
ObjectReader reader = tw.getObjectReader();
|
long modified = commit.getAuthorIdent().getWhen().getTime();
|
while (tw.next()) {
|
FileMode mode = tw.getFileMode(0);
|
if (mode == FileMode.GITLINK || mode == FileMode.TREE) {
|
continue;
|
}
|
tw.getObjectId(id, 0);
|
|
ZipArchiveEntry entry = new ZipArchiveEntry(tw.getPathString());
|
entry.setSize(reader.getObjectSize(id, Constants.OBJ_BLOB));
|
entry.setComment(commit.getName());
|
entry.setUnixMode(mode.getBits());
|
entry.setTime(modified);
|
zos.putArchiveEntry(entry);
|
|
ObjectLoader ldr = repository.open(id);
|
ldr.copyTo(zos);
|
zos.closeArchiveEntry();
|
}
|
zos.finish();
|
success = true;
|
} catch (IOException e) {
|
error(e, repository, "{0} failed to zip files from commit {1}", commit.getName());
|
} finally {
|
tw.release();
|
rw.dispose();
|
}
|
return success;
|
}
|
|
/**
|
* tar the contents of the tree at the (optionally) specified revision and
|
* the (optionally) specified basepath to the supplied outputstream.
|
*
|
* @param repository
|
* @param basePath
|
* if unspecified, entire repository is assumed.
|
* @param objectId
|
* if unspecified, HEAD is assumed.
|
* @param os
|
* @return true if repository was successfully zipped to supplied output
|
* stream
|
*/
|
public static boolean tar(Repository repository, String basePath, String objectId,
|
OutputStream os) {
|
return tar(null, repository, basePath, objectId, os);
|
}
|
|
/**
|
* tar.gz the contents of the tree at the (optionally) specified revision and
|
* the (optionally) specified basepath to the supplied outputstream.
|
*
|
* @param repository
|
* @param basePath
|
* if unspecified, entire repository is assumed.
|
* @param objectId
|
* if unspecified, HEAD is assumed.
|
* @param os
|
* @return true if repository was successfully zipped to supplied output
|
* stream
|
*/
|
public static boolean gz(Repository repository, String basePath, String objectId,
|
OutputStream os) {
|
return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os);
|
}
|
|
/**
|
* tar.xz the contents of the tree at the (optionally) specified revision and
|
* the (optionally) specified basepath to the supplied outputstream.
|
*
|
* @param repository
|
* @param basePath
|
* if unspecified, entire repository is assumed.
|
* @param objectId
|
* if unspecified, HEAD is assumed.
|
* @param os
|
* @return true if repository was successfully zipped to supplied output
|
* stream
|
*/
|
public static boolean xz(Repository repository, String basePath, String objectId,
|
OutputStream os) {
|
return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os);
|
}
|
|
/**
|
* tar.bzip2 the contents of the tree at the (optionally) specified revision and
|
* the (optionally) specified basepath to the supplied outputstream.
|
*
|
* @param repository
|
* @param basePath
|
* if unspecified, entire repository is assumed.
|
* @param objectId
|
* if unspecified, HEAD is assumed.
|
* @param os
|
* @return true if repository was successfully zipped to supplied output
|
* stream
|
*/
|
public static boolean bzip2(Repository repository, String basePath, String objectId,
|
OutputStream os) {
|
|
return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os);
|
}
|
|
/**
|
* Compresses/archives the contents of the tree at the (optionally)
|
* specified revision and the (optionally) specified basepath to the
|
* supplied outputstream.
|
*
|
* @param algorithm
|
* compression algorithm for tar (optional)
|
* @param repository
|
* @param basePath
|
* if unspecified, entire repository is assumed.
|
* @param objectId
|
* if unspecified, HEAD is assumed.
|
* @param os
|
* @return true if repository was successfully zipped to supplied output
|
* stream
|
*/
|
private static boolean tar(String algorithm, Repository repository, String basePath, String objectId,
|
OutputStream os) {
|
RevCommit commit = JGitUtils.getCommit(repository, objectId);
|
if (commit == null) {
|
return false;
|
}
|
|
OutputStream cos = os;
|
if (!StringUtils.isEmpty(algorithm)) {
|
try {
|
cos = new CompressorStreamFactory().createCompressorOutputStream(algorithm, os);
|
} catch (CompressorException e1) {
|
error(e1, repository, "{0} failed to open {1} stream", algorithm);
|
}
|
}
|
boolean success = false;
|
RevWalk rw = new RevWalk(repository);
|
TreeWalk tw = new TreeWalk(repository);
|
try {
|
tw.reset();
|
tw.addTree(commit.getTree());
|
TarArchiveOutputStream tos = new TarArchiveOutputStream(cos);
|
tos.setAddPaxHeadersForNonAsciiNames(true);
|
tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
if (!StringUtils.isEmpty(basePath)) {
|
PathFilter f = PathFilter.create(basePath);
|
tw.setFilter(f);
|
}
|
tw.setRecursive(true);
|
MutableObjectId id = new MutableObjectId();
|
long modified = commit.getAuthorIdent().getWhen().getTime();
|
while (tw.next()) {
|
FileMode mode = tw.getFileMode(0);
|
if (mode == FileMode.GITLINK || mode == FileMode.TREE) {
|
continue;
|
}
|
tw.getObjectId(id, 0);
|
|
ObjectLoader loader = repository.open(id);
|
if (FileMode.SYMLINK == mode) {
|
TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString(),TarArchiveEntry.LF_SYMLINK);
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
loader.copyTo(bos);
|
entry.setLinkName(bos.toString());
|
entry.setModTime(modified);
|
tos.putArchiveEntry(entry);
|
tos.closeArchiveEntry();
|
} else {
|
TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString());
|
entry.setMode(mode.getBits());
|
entry.setModTime(modified);
|
entry.setSize(loader.getSize());
|
tos.putArchiveEntry(entry);
|
loader.copyTo(tos);
|
tos.closeArchiveEntry();
|
}
|
}
|
tos.finish();
|
tos.close();
|
cos.close();
|
success = true;
|
} catch (IOException e) {
|
error(e, repository, "{0} failed to {1} stream files from commit {2}", algorithm, commit.getName());
|
} finally {
|
tw.release();
|
rw.dispose();
|
}
|
return success;
|
}
|
}
|