mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-05-05 13:57:27 +02:00
merge with branch repository-browser
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<jgit.version>0.12.1</jgit.version>
|
||||
<jgit.version>1.0.0.201106090707-r</jgit.version>
|
||||
</properties>
|
||||
|
||||
<!-- for jgit -->
|
||||
|
||||
@@ -41,12 +41,9 @@ import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RepositoryCache;
|
||||
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -107,17 +104,20 @@ public class GitChangesetViewer implements ChangesetViewer
|
||||
ChangesetPagingResult changesets = null;
|
||||
File directory = handler.getDirectory(repository);
|
||||
org.eclipse.jgit.lib.Repository gr = null;
|
||||
TreeWalk treeWalk = null;
|
||||
|
||||
try
|
||||
{
|
||||
gr = RepositoryCache.open(FileKey.lenient(directory, FS.DETECTED), true);
|
||||
gr = GitUtil.open(directory);
|
||||
|
||||
if (!gr.getAllRefs().isEmpty())
|
||||
{
|
||||
Git git = new Git(gr);
|
||||
List<Changeset> changesetList = new ArrayList<Changeset>();
|
||||
int counter = 0;
|
||||
TreeWalk treeWalk = new TreeWalk(gr);
|
||||
|
||||
treeWalk = new TreeWalk(gr);
|
||||
|
||||
Map<ObjectId, String> tags = createTagMap(gr);
|
||||
|
||||
for (RevCommit commit : git.log().call())
|
||||
@@ -130,7 +130,6 @@ public class GitChangesetViewer implements ChangesetViewer
|
||||
counter++;
|
||||
}
|
||||
|
||||
treeWalk.release();
|
||||
changesets = new ChangesetPagingResult(counter, changesetList);
|
||||
}
|
||||
}
|
||||
@@ -144,10 +143,8 @@ public class GitChangesetViewer implements ChangesetViewer
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (gr != null)
|
||||
{
|
||||
gr.close();
|
||||
}
|
||||
GitUtil.release(treeWalk);
|
||||
GitUtil.close(gr);
|
||||
}
|
||||
|
||||
return changesets;
|
||||
@@ -202,10 +199,7 @@ public class GitChangesetViewer implements ChangesetViewer
|
||||
throws IOException
|
||||
{
|
||||
String id = commit.getId().abbreviate(ID_LENGTH).name();
|
||||
long date = commit.getCommitTime();
|
||||
|
||||
date = date * 1000;
|
||||
|
||||
long date = GitUtil.getCommitTime(commit);
|
||||
PersonIdent authorIndent = commit.getCommitterIdent();
|
||||
Person author = new Person(authorIndent.getName(),
|
||||
authorIndent.getEmailAddress());
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitRepositoryBrowser implements RepositoryBrowser
|
||||
{
|
||||
|
||||
/** the logger for GitRepositoryBrowser */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GitRepositoryBrowser.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param handler
|
||||
* @param repository
|
||||
*/
|
||||
public GitRepositoryBrowser(GitRepositoryHandler handler,
|
||||
Repository repository)
|
||||
{
|
||||
this.handler = handler;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
* @param path
|
||||
* @param output
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public void getContent(String revision, String path, OutputStream output)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
File directory = handler.getDirectory(repository);
|
||||
org.eclipse.jgit.lib.Repository repo = GitUtil.open(directory);
|
||||
TreeWalk treeWalk = null;
|
||||
RevWalk revWalk = null;
|
||||
|
||||
try
|
||||
{
|
||||
treeWalk = new TreeWalk(repo);
|
||||
treeWalk.setRecursive(Util.nonNull(path).contains("/"));
|
||||
|
||||
ObjectId revId = GitUtil.getRevisionId(repo, revision);
|
||||
|
||||
revWalk = new RevWalk(repo);
|
||||
|
||||
RevCommit entry = revWalk.parseCommit(revId);
|
||||
RevTree revTree = entry.getTree();
|
||||
|
||||
treeWalk.addTree(revTree);
|
||||
treeWalk.setFilter(PathFilter.create(path));
|
||||
|
||||
if (treeWalk.next())
|
||||
{
|
||||
|
||||
// Path exists
|
||||
if (treeWalk.getFileMode(0).getObjectType() == Constants.OBJ_BLOB)
|
||||
{
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
ObjectLoader loader = repo.open(blobId);
|
||||
|
||||
loader.copyTo(output);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Not a blob, its something else (tree, gitlink)
|
||||
throw new PathNotFoundException(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PathNotFoundException(path);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.release(treeWalk);
|
||||
GitUtil.close(repo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public BrowserResult getResult(String revision, String path)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
BrowserResult result = null;
|
||||
File directory = handler.getDirectory(repository);
|
||||
org.eclipse.jgit.lib.Repository repo = GitUtil.open(directory);
|
||||
RevWalk revWalk = null;
|
||||
TreeWalk treeWalk = null;
|
||||
|
||||
try
|
||||
{
|
||||
ObjectId revId = GitUtil.getRevisionId(repo, revision);
|
||||
|
||||
treeWalk = new TreeWalk(repo);
|
||||
revWalk = new RevWalk(repo);
|
||||
treeWalk.addTree(revWalk.parseTree(revId));
|
||||
result = new BrowserResult();
|
||||
|
||||
List<FileObject> files = new ArrayList<FileObject>();
|
||||
|
||||
if (Util.isEmpty(path))
|
||||
{
|
||||
while (treeWalk.next())
|
||||
{
|
||||
files.add(createFileObject(repo, revId, treeWalk));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String[] parts = path.split("/");
|
||||
int current = 0;
|
||||
int limit = parts.length;
|
||||
|
||||
while (treeWalk.next())
|
||||
{
|
||||
String name = treeWalk.getNameString();
|
||||
|
||||
if (current >= limit)
|
||||
{
|
||||
String p = treeWalk.getPathString();
|
||||
|
||||
if (p.split("/").length > limit)
|
||||
{
|
||||
files.add(createFileObject(repo, revId, treeWalk));
|
||||
}
|
||||
}
|
||||
else if (name.equalsIgnoreCase(parts[current]))
|
||||
{
|
||||
current++;
|
||||
treeWalk.enterSubtree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.setFiles(files);
|
||||
result.setRevision(revId.getName());
|
||||
}
|
||||
finally
|
||||
{
|
||||
GitUtil.close(repo);
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.release(treeWalk);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param revId
|
||||
* @param treeWalk
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, TreeWalk treeWalk)
|
||||
throws IOException
|
||||
{
|
||||
FileObject file = new FileObject();
|
||||
String path = treeWalk.getPathString();
|
||||
|
||||
file.setName(treeWalk.getNameString());
|
||||
file.setPath(path);
|
||||
|
||||
ObjectLoader loader = repo.open(treeWalk.getObjectId(0));
|
||||
|
||||
file.setDirectory(loader.getType() == Constants.OBJ_TREE);
|
||||
file.setLength(loader.getSize());
|
||||
|
||||
// don't show message and date for directories to improve performance
|
||||
if (!file.isDirectory())
|
||||
{
|
||||
RevCommit commit = getLatestCommit(repo, revId, path);
|
||||
|
||||
if (commit != null)
|
||||
{
|
||||
file.setLastModified(GitUtil.getCommitTime(commit));
|
||||
file.setDescription(commit.getShortMessage());
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("could not find latest commit for {} on {}", path, revId);
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param revId
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private RevCommit getLatestCommit(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, String path)
|
||||
{
|
||||
RevCommit result = null;
|
||||
RevWalk walk = null;
|
||||
|
||||
try
|
||||
{
|
||||
walk = new RevWalk(repo);
|
||||
walk.setTreeFilter(AndTreeFilter.create(PathFilter.create(path),
|
||||
TreeFilter.ANY_DIFF));
|
||||
|
||||
RevCommit commit = walk.parseCommit(revId);
|
||||
|
||||
walk.markStart(commit);
|
||||
result = Util.getFirst(walk);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("could not parse commit for file", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
GitUtil.release(walk);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private GitRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
private Repository repository;
|
||||
}
|
||||
@@ -119,6 +119,22 @@ public class GitRepositoryHandler
|
||||
return changesetViewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public RepositoryBrowser getRepositoryBrowser(Repository repository)
|
||||
{
|
||||
AssertUtil.assertIsNotNull(repository);
|
||||
|
||||
return new GitRepositoryBrowser(this, repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.lib.RepositoryCache;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
*/
|
||||
public static void close(org.eclipse.jgit.lib.Repository repo)
|
||||
{
|
||||
if (repo != null)
|
||||
{
|
||||
repo.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param walk
|
||||
*/
|
||||
public static void release(TreeWalk walk)
|
||||
{
|
||||
if (walk != null)
|
||||
{
|
||||
walk.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param walk
|
||||
*/
|
||||
public static void release(RevWalk walk)
|
||||
{
|
||||
if (walk != null)
|
||||
{
|
||||
walk.release();
|
||||
}
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param commit
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static long getCommitTime(RevCommit commit)
|
||||
{
|
||||
long date = commit.getCommitTime();
|
||||
|
||||
date = date * 1000;
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param directory
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static org.eclipse.jgit.lib.Repository open(File directory)
|
||||
throws IOException
|
||||
{
|
||||
return RepositoryCache.open(RepositoryCache.FileKey.lenient(directory,
|
||||
FS.DETECTED), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param revision
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ObjectId getRevisionId(org.eclipse.jgit.lib.Repository repo,
|
||||
String revision)
|
||||
throws IOException
|
||||
{
|
||||
ObjectId revId = null;
|
||||
|
||||
if (Util.isNotEmpty(revision))
|
||||
{
|
||||
revId = repo.resolve(revision);
|
||||
}
|
||||
else
|
||||
{
|
||||
revId = repo.resolve(Constants.HEAD);
|
||||
}
|
||||
|
||||
return revId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgRepositoryBrowser implements RepositoryBrowser
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String DEFAULT_REVISION = "tip";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_PATH = "SCM_PATH";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_PYTHON_PATH = "SCM_PYTHON_PATH";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_REVISION = "SCM_REVISION";
|
||||
|
||||
/** Field description */
|
||||
public static final String RESOURCE_BROWSE = "/sonia/scm/hgbrowse.py";
|
||||
|
||||
/** the logger for HgRepositoryBrowser */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HgRepositoryBrowser.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param handler
|
||||
* @param repository
|
||||
* @param browserResultContext
|
||||
*/
|
||||
public HgRepositoryBrowser(HgRepositoryHandler handler,
|
||||
Repository repository,
|
||||
JAXBContext browserResultContext)
|
||||
{
|
||||
this.handler = handler;
|
||||
this.repository = repository;
|
||||
this.browserResultContext = browserResultContext;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
* @param path
|
||||
* @param output
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public void getContent(String revision, String path, OutputStream output)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
if (Util.isEmpty(revision))
|
||||
{
|
||||
revision = DEFAULT_REVISION;
|
||||
}
|
||||
|
||||
File directory = handler.getDirectory(repository);
|
||||
ProcessBuilder builder =
|
||||
new ProcessBuilder(handler.getConfig().getHgBinary(), "cat", "-r",
|
||||
revision, Util.nonNull(path));
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
StringBuilder msg = new StringBuilder();
|
||||
|
||||
for (String param : builder.command())
|
||||
{
|
||||
msg.append(param).append(" ");
|
||||
}
|
||||
|
||||
logger.debug(msg.toString());
|
||||
}
|
||||
|
||||
Process p = builder.directory(directory).start();
|
||||
InputStream input = null;
|
||||
|
||||
try
|
||||
{
|
||||
input = p.getInputStream();
|
||||
IOUtil.copy(input, output);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public BrowserResult getResult(String revision, String path)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
HgConfig config = handler.getConfig();
|
||||
ProcessBuilder pb = new ProcessBuilder(config.getPythonBinary());
|
||||
Map<String, String> env = pb.environment();
|
||||
|
||||
env.put(ENV_PYTHON_PATH, Util.nonNull(config.getPythonPath()));
|
||||
|
||||
String directory = handler.getDirectory(repository).getAbsolutePath();
|
||||
|
||||
env.put(ENV_REPOSITORY_PATH, directory);
|
||||
|
||||
if (Util.isEmpty(revision))
|
||||
{
|
||||
revision = DEFAULT_REVISION;
|
||||
}
|
||||
|
||||
env.put(ENV_REVISION, revision);
|
||||
env.put(ENV_PATH, Util.nonNull(path));
|
||||
|
||||
Process p = pb.start();
|
||||
BrowserResult result = null;
|
||||
InputStream resource = null;
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
|
||||
try
|
||||
{
|
||||
resource = HgRepositoryBrowser.class.getResourceAsStream(RESOURCE_BROWSE);
|
||||
output = p.getOutputStream();
|
||||
IOUtil.copy(resource, output);
|
||||
output.close();
|
||||
|
||||
// IOUtil.copy(p.getErrorStream(), System.err);
|
||||
input = p.getInputStream();
|
||||
result =
|
||||
(BrowserResult) browserResultContext.createUnmarshaller().unmarshal(
|
||||
input);
|
||||
|
||||
// IOUtil.copy(input, System.out);
|
||||
input.close();
|
||||
}
|
||||
catch (JAXBException ex)
|
||||
{
|
||||
logger.error("could not parse result", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(resource);
|
||||
IOUtil.close(input);
|
||||
IOUtil.close(output);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private JAXBContext browserResultContext;
|
||||
|
||||
/** Field description */
|
||||
private HgRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
private Repository repository;
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import com.google.inject.Singleton;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.ConfigurationException;
|
||||
import sonia.scm.Type;
|
||||
import sonia.scm.installer.HgInstaller;
|
||||
import sonia.scm.installer.HgInstallerFactory;
|
||||
@@ -59,6 +60,10 @@ import sonia.scm.web.HgWebConfigWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -95,6 +100,15 @@ public class HgRepositoryHandler
|
||||
public HgRepositoryHandler(StoreFactory storeFactory, FileSystem fileSystem)
|
||||
{
|
||||
super(storeFactory, fileSystem);
|
||||
|
||||
try
|
||||
{
|
||||
this.browserResultContext = JAXBContext.newInstance(BrowserResult.class);
|
||||
}
|
||||
catch (JAXBException ex)
|
||||
{
|
||||
throw new ConfigurationException("could not create jaxbcontext", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -181,6 +195,20 @@ public class HgRepositoryHandler
|
||||
return changesetViewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public RepositoryBrowser getRepositoryBrowser(Repository repository)
|
||||
{
|
||||
return new HgRepositoryBrowser(this, repository, browserResultContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -254,4 +282,9 @@ public class HgRepositoryHandler
|
||||
{
|
||||
return HgConfig.class;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private JAXBContext browserResultContext;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import os
|
||||
|
||||
pythonPath = os.environ['SCM_PYTHON_PATH']
|
||||
|
||||
if len(pythonPath) > 0:
|
||||
pathParts = pythonPath.split(os.pathsep)
|
||||
for i in range(len(pathParts)):
|
||||
sys.path.insert(i, pathParts[i])
|
||||
|
||||
|
||||
from mercurial import hg, ui
|
||||
import datetime, time
|
||||
|
||||
def getName(path):
|
||||
parts = path.split('/')
|
||||
length = len(parts)
|
||||
if path.endswith('/'):
|
||||
length =- 1
|
||||
return parts[length - 1]
|
||||
|
||||
repositoryPath = os.environ['SCM_REPOSITORY_PATH']
|
||||
|
||||
revision = os.environ['SCM_REVISION']
|
||||
path = os.environ['SCM_PATH']
|
||||
name = getName(path)
|
||||
length = 0
|
||||
paths = []
|
||||
repo = hg.repository(ui.ui(), path = repositoryPath)
|
||||
mf = repo[revision].manifest()
|
||||
|
||||
if path is "":
|
||||
length = 1
|
||||
for f in mf:
|
||||
paths.append(f)
|
||||
else:
|
||||
length = len(path.split('/')) + 1
|
||||
for f in mf:
|
||||
if f.startswith(path):
|
||||
paths.append(f)
|
||||
|
||||
files = []
|
||||
directories = []
|
||||
|
||||
for p in paths:
|
||||
parts = p.split('/')
|
||||
depth = len(parts)
|
||||
if depth is length:
|
||||
file = repo[revision][p]
|
||||
files.append(file)
|
||||
elif depth > length:
|
||||
dirpath = ''
|
||||
for i in range(0, length):
|
||||
dirpath += parts[i] + '/'
|
||||
if not dirpath in directories:
|
||||
directories.append(dirpath)
|
||||
|
||||
print '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||||
print '<browser-result>'
|
||||
print ' <revision>' + revision + '</revision>'
|
||||
# todo print tag, and branch
|
||||
print ' <files>'
|
||||
for dir in directories:
|
||||
print ' <file>'
|
||||
print ' <name>' + getName(dir) + '</name>'
|
||||
print ' <path>' + dir + '</path>'
|
||||
print ' <directory>true</directory>'
|
||||
print ' </file>'
|
||||
|
||||
for file in files:
|
||||
linkrev = repo[file.linkrev()]
|
||||
time = int(linkrev.date()[0]) * 1000
|
||||
desc = linkrev.description()
|
||||
print ' <file>'
|
||||
print ' <name>' + getName(file.path()) + '</name>'
|
||||
print ' <path>' + file.path() + '</path>'
|
||||
print ' <directory>false</directory>'
|
||||
print ' <length>' + str(file.size()) + '</length>'
|
||||
print ' <lastModified>' + str(time).split('.')[0] + '</lastModified>'
|
||||
print ' <description>' + desc + '</description>'
|
||||
print ' </file>'
|
||||
print ' </files>'
|
||||
print '</browser-result>'
|
||||
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNDirEntry;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.SVNNodeKind;
|
||||
import org.tmatesoft.svn.core.SVNProperties;
|
||||
import org.tmatesoft.svn.core.SVNURL;
|
||||
import org.tmatesoft.svn.core.io.SVNRepository;
|
||||
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class SvnRepositoryBrowser implements RepositoryBrowser
|
||||
{
|
||||
|
||||
/** the logger for SvnRepositoryBrowser */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(SvnRepositoryBrowser.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param handler
|
||||
* @param repository
|
||||
*/
|
||||
public SvnRepositoryBrowser(SvnRepositoryHandler handler,
|
||||
Repository repository)
|
||||
{
|
||||
this.handler = handler;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* http://wiki.svnkit.com/Printing_Out_File_Contents
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
* @param path
|
||||
* @param output
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public void getContent(String revision, String path, OutputStream output)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
long revisionNumber = getRevisionNumber(revision);
|
||||
SVNRepository svnRepository = null;
|
||||
|
||||
try
|
||||
{
|
||||
svnRepository = getSvnRepository();
|
||||
svnRepository.getFile(path, revisionNumber, new SVNProperties(), output);
|
||||
}
|
||||
catch (SVNException ex)
|
||||
{
|
||||
logger.error("could not open repository", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(svnRepository);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public BrowserResult getResult(String revision, String path)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
long revisionNumber = getRevisionNumber(revision);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("browser repository {} in path {} at revision {}",
|
||||
new Object[] { repository.getName(),
|
||||
path, revision });
|
||||
}
|
||||
|
||||
BrowserResult result = null;
|
||||
SVNRepository svnRepository = null;
|
||||
|
||||
try
|
||||
{
|
||||
svnRepository = getSvnRepository();
|
||||
|
||||
Collection<SVNDirEntry> entries =
|
||||
svnRepository.getDir(Util.nonNull(path), revisionNumber, null,
|
||||
(Collection) null);
|
||||
List<FileObject> children = new ArrayList<FileObject>();
|
||||
String basePath = Util.EMPTY_STRING;
|
||||
|
||||
if (Util.isNotEmpty(path))
|
||||
{
|
||||
basePath = path;
|
||||
|
||||
if (!basePath.endsWith("/"))
|
||||
{
|
||||
basePath = basePath.concat("/");
|
||||
}
|
||||
}
|
||||
|
||||
for (SVNDirEntry entry : entries)
|
||||
{
|
||||
children.add(createFileObject(entry, basePath));
|
||||
}
|
||||
|
||||
result = new BrowserResult();
|
||||
result.setRevision(revision);
|
||||
result.setFiles(children);
|
||||
}
|
||||
catch (SVNException ex)
|
||||
{
|
||||
logger.error("could not open repository", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(svnRepository);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param svnRepository
|
||||
*/
|
||||
private void close(SVNRepository svnRepository)
|
||||
{
|
||||
if (svnRepository != null)
|
||||
{
|
||||
svnRepository.closeSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param entry
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private FileObject createFileObject(SVNDirEntry entry, String path)
|
||||
{
|
||||
FileObject fileObject = new FileObject();
|
||||
|
||||
fileObject.setName(entry.getName());
|
||||
fileObject.setPath(path.concat(entry.getRelativePath()));
|
||||
fileObject.setDirectory(entry.getKind() == SVNNodeKind.DIR);
|
||||
|
||||
if (entry.getDate() != null)
|
||||
{
|
||||
fileObject.setLastModified(entry.getDate().getTime());
|
||||
}
|
||||
|
||||
fileObject.setLength(entry.getSize());
|
||||
fileObject.setDescription(entry.getCommitMessage());
|
||||
|
||||
return fileObject;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param revision
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private long getRevisionNumber(String revision) throws RepositoryException
|
||||
{
|
||||
long revisionNumber = -1;
|
||||
|
||||
if (Util.isNotEmpty(revision))
|
||||
{
|
||||
try
|
||||
{
|
||||
revisionNumber = Long.parseLong(revision);
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
throw new RepositoryException("given revision is not a svnrevision");
|
||||
}
|
||||
}
|
||||
|
||||
return revisionNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws SVNException
|
||||
*/
|
||||
private SVNRepository getSvnRepository() throws SVNException
|
||||
{
|
||||
File directory = handler.getDirectory(repository);
|
||||
|
||||
return SVNRepositoryFactory.create(SVNURL.fromFile(directory));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private SvnRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
private Repository repository;
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
|
||||
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
|
||||
|
||||
import sonia.scm.Type;
|
||||
@@ -91,6 +92,9 @@ public class SvnRepositoryHandler
|
||||
public SvnRepositoryHandler(StoreFactory storeFactory, FileSystem fileSystem)
|
||||
{
|
||||
super(storeFactory, fileSystem);
|
||||
|
||||
// setup FSRepositoryFactory for SvnRepositoryBrowser
|
||||
FSRepositoryFactory.setup();
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
@@ -126,6 +130,22 @@ public class SvnRepositoryHandler
|
||||
return changesetViewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public RepositoryBrowser getRepositoryBrowser(Repository repository)
|
||||
{
|
||||
AssertUtil.assertIsNotNull(repository);
|
||||
|
||||
return new SvnRepositoryBrowser(this, repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user