All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.madgag.git.package.scala Maven / Gradle / Ivy

There is a newer version: 3.5
Show newest version
/*
 * Copyright 2015 Roberto Tyley
 *
 * 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.madgag

import java.io.File
import java.nio.charset.Charset

import org.eclipse.jgit
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm
import org.eclipse.jgit.diff._
import org.eclipse.jgit.internal.storage.file.ObjectDirectory
import org.eclipse.jgit.lib.Constants.OBJ_BLOB
import org.eclipse.jgit.lib.ObjectInserter.Formatter
import org.eclipse.jgit.lib.ObjectReader.OBJ_ANY
import org.eclipse.jgit.lib._
import org.eclipse.jgit.revwalk._
import org.eclipse.jgit.treewalk.TreeWalk
import org.eclipse.jgit.treewalk.filter.{AndTreeFilter, TreeFilter}
import org.eclipse.jgit.util.FS

import scala.annotation.tailrec
import scala.collection.convert.wrapAsScala._
import scala.language.implicitConversions
import scala.util.{Success, Try}


package object git {

  implicit class RichByteArray(bytes: Array[Byte]) {
    lazy val blobId = (new Formatter).idFor(OBJ_BLOB, bytes)
  }

  def storeBlob(bytes: Array[Byte])(implicit i: ObjectInserter): ObjectId = i.insert(OBJ_BLOB, bytes)

  def storeBlob(text: String)(implicit i: ObjectInserter, charset: Charset): ObjectId = storeBlob(text.getBytes(charset))

  def walk(trees: RevTree*)(
    filter: TreeFilter,
    recursive: Boolean = true,
    postOrderTraversal: Boolean = false)(implicit reader: ObjectReader) = {

    val tw = new TreeWalk(reader)
    tw.setRecursive(recursive)
    tw.setPostOrderTraversal(postOrderTraversal)
    tw.reset

    for (t <- trees) {
      tw.addTree(t)
    }

    tw.setFilter(filter)

    tw
  }

  implicit class RichObjectDatabase(objectDatabase: ObjectDatabase) {

    lazy val threadLocalResources = new ThreadLocalObjectDatabaseResources(objectDatabase)

  }

  implicit class RichObjectDirectory(objectDirectory: ObjectDirectory) {

    def packedObjects: Iterable[ObjectId] =
      for { pack <- objectDirectory.getPacks ; entry <- pack } yield entry.toObjectId

  }

  implicit class RichRepo(repo: Repository) {
    lazy val git = new Git(repo)

    lazy val topDirectory = if (repo.isBare) repo.getDirectory else repo.getWorkTree

    def singleThreadedReaderTuple = {
      val revWalk=new RevWalk(repo)
      (revWalk, revWalk.getObjectReader)
    }

    def nonSymbolicRefs = repo.getAllRefs.values.filterNot(_.isSymbolic)

  }

  implicit class RichString(str: String) {
    def asObjectId = jgit.lib.ObjectId.fromString(str)
  }

  implicit class RichRevTree(revTree: RevTree) {
    def walk(postOrderTraversal: Boolean = false)(implicit reader: ObjectReader) = {
      val treeWalk = new TreeWalk(reader)
      treeWalk.setRecursive(true)
      treeWalk.setPostOrderTraversal(postOrderTraversal)
      treeWalk.addTree(revTree)
      treeWalk
    }
  }

  implicit def treeWalkPredicateToTreeFilter(p: TreeWalk => Boolean): TreeFilter = new TreeFilter() {
    def include(walker: TreeWalk) = p(walker)

    def shouldBeRecursive() = true

    override def clone() = this
  }

  implicit class RichTreeWalk(treeWalk: TreeWalk) {

    /**
     * @param f - note that the function must completely extract all
     *          information from the TreeWalk at the point of
     *          execution, the state of TreeWalk will be altered after
     *          execution.
     */
    def map[V](f: TreeWalk => V): Iterator[V] = new Iterator[V] {
      var _hasNext = treeWalk.next()

      def hasNext = _hasNext

      def next() = {
        val v = f(treeWalk)
        _hasNext = treeWalk.next()
        v
      }
    }
    // def flatMap[B](f: TreeWalk => Iterator[B]): C[B]

    def withFilter(p: TreeWalk => Boolean): TreeWalk = {
      treeWalk.setFilter(AndTreeFilter.create(treeWalk.getFilter, p))
      treeWalk
    }


    def foreach[U](f: TreeWalk => U) {
      while (treeWalk.next()) {
        f(treeWalk)
      }
    }

    def exists(p: TreeWalk => Boolean): Boolean = {
      var res = false
      while (!res && treeWalk.next()) res = p(treeWalk)
      res
    }

    def slashPrefixedPath = "/" + treeWalk.getPathString
  }

  implicit class RichRef(ref: Ref) {
    def targetObjectId(implicit refDatabase: RefDatabase): ObjectId = {
      val peeledRef = refDatabase.peel(ref)
      Option(peeledRef.getPeeledObjectId).getOrElse(peeledRef.getObjectId)
    }
  }

  implicit class RichRevObject(revObject: RevObject) {
    lazy val typeString = Constants.typeString(revObject.getType)

    def toTree(implicit revWalk: RevWalk): Option[RevTree] = treeOrBlobPointedToBy(revObject).right.toOption
  }

  val FileModeNames = Map(
    FileMode.EXECUTABLE_FILE -> "executable",
    FileMode.REGULAR_FILE -> "regular-file",
    FileMode.SYMLINK -> "symlink",
    FileMode.TREE -> "tree",
    FileMode.MISSING -> "missing",
    FileMode.GITLINK -> "submodule"
  )

  implicit class RichFileMode(fileMode: FileMode) {
    lazy val name = FileModeNames(fileMode)
  }

  implicit class RichDiffEntry(diffEntry: DiffEntry) {
    import DiffEntry.Side
    import Side.{NEW, OLD}

    def isDiffableType(side: Side) =
      // diffEntry.getMode(side) != FileMode.GITLINK &&
        diffEntry.getId(side) != null && diffEntry.getMode(side).getObjectType == OBJ_BLOB

    lazy val bothSidesDiffableType: Boolean = Side.values().map(isDiffableType).forall(d => d)

    def editList(implicit objectReader: ObjectReader): Option[EditList] = {
      def rawText(side: Side) = {
        objectReader.resolveExistingUniqueId(diffEntry.getId(side)).map(_.open).toOption.filterNot(_.isLarge).flatMap {
          l =>
            val bytes = l.getCachedBytes
            if (RawText.isBinary(bytes)) None else Some(new RawText(bytes))
        }
      }

      if (bothSidesDiffableType) {
        for (oldText <- rawText(OLD) ; newText <- rawText(NEW)) yield {
          val algo = DiffAlgorithm.getAlgorithm(SupportedAlgorithm.HISTOGRAM)
          val comp = RawTextComparator.DEFAULT
          algo.diff(comp, oldText, newText)
        }
      } else None
    }
  }

  implicit class RichObjectId(objectId: AnyObjectId) {
    def open(implicit objectReader: ObjectReader): ObjectLoader = objectReader.open(objectId)

    def sizeOpt(implicit objectReader: ObjectReader): Option[Long] = sizeTry.toOption

    def sizeTry(implicit objectReader: ObjectReader): Try[Long] =
      Try(objectReader.getObjectSize(objectId, OBJ_ANY))

    def asRevObject(implicit revWalk: RevWalk) = revWalk.parseAny(objectId)

    def asRevCommit(implicit revWalk: RevWalk) = revWalk.parseCommit(objectId)

    def asRevCommitOpt(implicit revWalk: RevWalk): Option[RevCommit] =
      if (revWalk.getObjectReader.has(objectId)) Some(objectId.asRevCommit) else None

    def asRevTag(implicit revWalk: RevWalk) = revWalk.parseTag(objectId)

    def asRevTree(implicit revWalk: RevWalk) = revWalk.parseTree(objectId)

    lazy val shortName = objectId.getName.take(8)
  }

  implicit class RichObjectReader(reader: ObjectReader) {
    def resolveUniquely(id: AbbreviatedObjectId): Try[ObjectId] = Try(reader.resolve(id).toList).flatMap {
      _ match {
        case fullId :: Nil => Success(fullId)
        case ids => val resolution = if (ids.isEmpty) "no Git object" else s"${ids.size} objects : ${ids.map(reader.abbreviate).map(_.name).mkString(",")}"
          throw new IllegalArgumentException(s"Abbreviated id '${id.name}' resolves to $resolution")
      }
    }

    def resolveExistingUniqueId(id: AbbreviatedObjectId) = resolveUniquely(id).flatMap {
      fullId => if (reader.has(fullId)) Success(fullId) else throw new IllegalArgumentException(s"Id '$id' not found in repo")
    }
  }

  def abbrId(str: String)(implicit reader: ObjectReader): ObjectId = reader.resolveExistingUniqueId(AbbreviatedObjectId.fromString(str)).get
    
  def resolveGitDirFor(folder: File) = Option(RepositoryCache.FileKey.resolve(folder, FS.detect)).filter(_.exists())

  def treeOrBlobPointedToBy(revObject: RevObject)(implicit revWalk: RevWalk): Either[RevBlob, RevTree] = revObject match {
    case commit: RevCommit => Right(commit.getTree)
    case tree: RevTree => Right(tree)
    case blob: RevBlob => Left(blob)
    case tag: RevTag => treeOrBlobPointedToBy(revWalk.peel(tag))
  }

  def diff(trees: RevTree*)(implicit reader: ObjectReader): Seq[DiffEntry] =
    DiffEntry.scan(walk(trees: _*)(TreeFilter.ANY_DIFF))

  def allBlobsUnder(tree: RevTree)(implicit reader: ObjectReader): Set[ObjectId] =
    tree.walk().map(_.getObjectId(0)).toSet

  // use ObjectWalk instead ??
  def allBlobsReachableFrom(revisions: Set[String])(implicit repo: Repository): Set[ObjectId] = {
    implicit val (revWalk, reader) = repo.singleThreadedReaderTuple

    revisions.map(repo.resolve).toSet.map {
      objectId: ObjectId => allBlobsReachableFrom(objectId.asRevObject)
    } reduce (_ ++ _)
  }

  @tailrec
  def allBlobsReachableFrom(revObject: RevObject)(implicit reader: ObjectReader): Set[ObjectId] = revObject match {
    case commit: RevCommit => allBlobsUnder(commit.getTree)
    case tree: RevTree => allBlobsUnder(tree)
    case blob: RevBlob => Set(blob)
    case tag: RevTag => allBlobsReachableFrom(tag.getObject)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy