Newer
Older
gitbucket_jkp / src / main / scala / util / JGitUtil.scala
package util

import org.eclipse.jgit.api.Git
import app.{RepositoryInfo, FileInfo}
import util.Directory._
import scala.collection.JavaConverters._
import javax.servlet.ServletContext
import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.errors.MissingObjectException
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.diff.DiffFormatter
import org.eclipse.jgit.treewalk.TreeWalk
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.diff.RawTextComparator
import org.eclipse.jgit.util.io.DisabledOutputStream
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.revwalk.RevSort
import org.eclipse.jgit.diff.DiffEntry.ChangeType
import org.eclipse.jgit.lib.FileMode
import org.eclipse.jgit.treewalk.filter.PathFilter

object JGitUtil {
  
  /**
   * Returns the repository information. It contains branch names and tag names.
   */
  def getRepositoryInfo(owner: String, repository: String, servletContext: ServletContext): RepositoryInfo = {
    val git = Git.open(getRepositoryDir(owner, repository))
    RepositoryInfo(
      owner, repository, "http://localhost:8080%s/git/%s/%s.git".format(servletContext.getContextPath, owner, repository),
      // branches
      git.branchList.call.toArray.map { ref =>
        ref.asInstanceOf[Ref].getName.replaceFirst("^refs/heads/", "")
      }.toList,
      // tags
      git.tagList.call.toArray.map { ref =>
        ref.asInstanceOf[Ref].getName
      }.toList
    )   
  }
  
  /**
   * Get the branch name from the commit id.
   */
  def getBranchNameFromCommitId(id: String, repositoryInfo: RepositoryInfo): String = {
      repositoryInfo.branchList.find { branch =>
        val git = Git.open(getBranchDir(repositoryInfo.owner, repositoryInfo.name, branch))
        git.log.add(ObjectId.fromString(id)).call.iterator.hasNext
      }.get
  }
  
  /**
   * Returns the file list of the specified path.
   * 
   * @param owner the repository owner
   * @param repository the repository name
   * @param revstr the branch name or commit id
   * @param path the directory path (optional)
   * @return HTML of the file list
   */
  def getFileList(owner: String, repository: String, revision: String, path: String = "."): List[FileInfo] = {
    val git = Git.open(getRepositoryDir(owner, repository))
      
    val revWalk = new RevWalk(git.getRepository)
    val objectId = git.getRepository.resolve(revision)
    val revCommit = revWalk.parseCommit(objectId)
      
    val treeWalk = new TreeWalk(git.getRepository)
    treeWalk.addTree(revCommit.getTree)
    if(path != "."){
      treeWalk.setRecursive(true)
      treeWalk.setFilter(PathFilter.create(path))
    }
      
    val list = new scala.collection.mutable.ListBuffer[FileInfo]
    
    while (treeWalk.next()) {
      val fileCommit = JGitUtil.getLatestCommitFromPath(git.getRepository, treeWalk.getPathString, revision)
      list.append(FileInfo(
          treeWalk.getObjectId(0),
          treeWalk.getFileMode(0) == FileMode.TREE, 
          treeWalk.getNameString,
          fileCommit.getCommitterIdent.getWhen,
          fileCommit.getShortMessage, 
          fileCommit.getCommitterIdent.getName)
      )
    }
    
    treeWalk.release
    revWalk.dispose
    
    list.toList.sortWith { (file1, file2) => (file1.isDirectory, file2.isDirectory) match {
      case (true , false) => true
      case (false, true ) => false
      case _ => file1.name.compareTo(file2.name) < 0
    }}
  }
  
  /**
   * Returns the latest RevCommit of the specified path.
   */
  def getLatestCommitFromPath(repository: Repository, path: String, revision: String): RevCommit = {
      val revWalk = new RevWalk(repository)
      revWalk.markStart(revWalk.parseCommit(repository.resolve(revision)))
      revWalk.sort(RevSort.REVERSE);
      val i = revWalk.iterator
      
      // TODO DON'T use var!
      var result: RevCommit = null
      
      while(i.hasNext){
        val commit = i.next
        if(commit.getParentCount == 0){
          // Initial commit
          val treeWalk = new TreeWalk(repository)
          treeWalk.reset()
          treeWalk.setRecursive(true)
          treeWalk.addTree(commit.getTree)
          while (treeWalk.next && result == null) {
            if(treeWalk.getPathString.startsWith(path)){
              result = commit
            }
          }
          treeWalk.release     
        } else {
          val parent = revWalk.parseCommit(commit.getParent(0).getId())
          val df = new DiffFormatter(DisabledOutputStream.INSTANCE)
          df.setRepository(repository)
          df.setDiffComparator(RawTextComparator.DEFAULT)
          df.setDetectRenames(true)
          val diffs = df.scan(parent.getTree(), commit.getTree)
          val find = diffs.asScala.find { diff =>
            val objectId = diff.getNewId.name
            (diff.getChangeType != ChangeType.DELETE && diff.getNewPath.startsWith(path))
          }
          if(find != None){
            result = commit
          }
        }
        revWalk.release
      }
      result
  }
  
  /**
   * Get object content of the given id as String from the Git repository.
   * 
   * @param git the Git object
   * @param id the object id
   * @param large if true then returns None for the large file
   * @return the object or None if object does not exist
   */
  def getContent(git: Git, id: ObjectId, large: Boolean): Option[Array[Byte]] = try {
    val loader = git.getRepository.getObjectDatabase.open(id)
    if(large == false && FileTypeUtil.isLarge(loader.getSize)){
      None
    } else {
      Some(git.getRepository.getObjectDatabase.open(id).getBytes)
    }
  } catch {
    case e: MissingObjectException => None
  }

}