app/lib/GitChanges.scala (38 lines of code) (raw):

package lib import com.madgag.git._ import com.madgag.scalagithub.model.PullRequest import org.eclipse.jgit.lib.ObjectReader import org.eclipse.jgit.revwalk.filter.RevFilter.MERGE_BASE import org.eclipse.jgit.revwalk.{RevCommit, RevWalk} import org.eclipse.jgit.treewalk.TreeWalk import org.eclipse.jgit.treewalk.filter.{AndTreeFilter, PathFilterGroup, TreeFilter} import scala.jdk.CollectionConverters._ object GitChanges { val treeDiffFilter: TreeFilter = (w: TreeWalk) => w.isSubtree && (1 until w.getTreeCount).exists(!w.idEqual(_, 0)) def affects(pullRequest: PullRequest, interestingPaths: Set[String])(implicit revWalk: RevWalk): Set[String] = for { tipCommit <- pullRequest.availableTipCommits affectedPath <- affectedFolders(pullRequest.base.asRevCommit, tipCommit, interestingPaths) } yield affectedPath /** * * @return interestingPaths which were affected by the changes base..head */ def affectedFolders(base: RevCommit, head: RevCommit, interestingPaths: Set[String])(implicit revWalk: RevWalk): Set[String] = { implicit val reader = revWalk.getObjectReader val pathsWhichDoNotStartAndEndWithSlashes = interestingPaths.filterNot(p => p.startsWith("/") && p.endsWith("/")) require(pathsWhichDoNotStartAndEndWithSlashes.isEmpty, s"Interesting paths should start and end with a slash: $pathsWhichDoNotStartAndEndWithSlashes") revWalk.reset() revWalk.setRevFilter(MERGE_BASE) revWalk.markStart(base) revWalk.markStart(head) val mergeBase = revWalk.next() differentFolders(head, mergeBase, interestingPaths) } private def differentFolders(head: RevCommit, mergeBase: RevCommit, interestingPaths: Set[String])(implicit reader: ObjectReader): Set[String] = { val (rootPaths, subFolderPaths) = interestingPaths.partition(_ == "/") val affectedRootPaths = rootPaths.filter(_ => mergeBase.getTree != head.getTree) val affectedSubFolderPaths = if (subFolderPaths.isEmpty) Set.empty else { val treeFilter = AndTreeFilter.create(PathFilterGroup.createFromStrings(subFolderPaths.map(_.stripPrefix("/")).asJava), treeDiffFilter) walk(mergeBase.getTree, head.getTree)(treeFilter, postOrderTraversal = true).map(_.slashPrefixedPath + "/").toSet.filter(subFolderPaths) } affectedRootPaths ++ affectedSubFolderPaths } }