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

gitbucket.core.service.RepositoryCommitFileService.scala Maven / Gradle / Ivy

The newest version!
package gitbucket.core.service
import gitbucket.core.api.JsonFormat
import gitbucket.core.model.{Account, WebHook}
import gitbucket.core.model.Profile.profile.blockingApi._
import gitbucket.core.model.activity.{CloseIssueInfo, PushInfo}
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.service.WebHookService.WebHookPushPayload
import gitbucket.core.util.Directory.getRepositoryDir
import gitbucket.core.util.JGitUtil.CommitInfo
import gitbucket.core.util.{JGitUtil, LockUtil}
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
import org.eclipse.jgit.lib._
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}

import scala.util.Using

trait RepositoryCommitFileService {
  self: AccountService
    with ActivityService
    with IssuesService
    with PullRequestService
    with WebHookPullRequestService
    with RepositoryService =>

  /**
   * Create multiple files by callback function.
   * Returns commitId.
   */
  def commitFiles(
    repository: RepositoryService.RepositoryInfo,
    branch: String,
    message: String,
    loginAccount: Account,
    settings: SystemSettings
  )(
    f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
  )(implicit s: Session, c: JsonFormat.Context): Either[String, ObjectId] = {
    _createFiles(repository, branch, message, loginAccount, loginAccount.fullName, loginAccount.mailAddress, settings)(
      f
    ).map(_._1)
  }

  /**
   * Create a file from string content.
   * Returns commitId + blobId.
   */
  def commitFile(
    repository: RepositoryService.RepositoryInfo,
    branch: String,
    path: String,
    newFileName: Option[String],
    oldFileName: Option[String],
    content: String,
    charset: String,
    message: String,
    commit: String,
    loginAccount: Account,
    settings: SystemSettings
  )(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, Option[ObjectId])] = {
    commitFile(
      repository,
      branch,
      path,
      newFileName,
      oldFileName,
      if (content.nonEmpty) { content.getBytes(charset) }
      else { Array.emptyByteArray },
      message,
      commit,
      loginAccount,
      loginAccount.fullName,
      loginAccount.mailAddress,
      settings
    )
  }

  /**
   * Create a file from byte array content.
   * Returns commitId + blobId.
   */
  def commitFile(
    repository: RepositoryService.RepositoryInfo,
    branch: String,
    path: String,
    newFileName: Option[String],
    oldFileName: Option[String],
    content: Array[Byte],
    message: String,
    commit: String,
    pusherAccount: Account,
    committerName: String,
    committerMailAddress: String,
    settings: SystemSettings
  )(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, Option[ObjectId])] = {

    val newPath = newFileName.map { newFileName =>
      if (path.length == 0) newFileName else s"${path}/${newFileName}"
    }
    val oldPath = oldFileName.map { oldFileName =>
      if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
    }

    _createFiles(repository, branch, message, pusherAccount, committerName, committerMailAddress, settings) {
      case (git, headTip, builder, inserter) =>
        if (headTip.getName == commit) {
          val permission = JGitUtil
            .processTree(git, headTip) { (path, tree) =>
              // Add all entries except the editing file
              if (!newPath.contains(path) && !oldPath.contains(path)) {
                builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
              }
              // Retrieve permission if file exists to keep it
              oldPath.collect { case x if x == path => tree.getEntryFileMode.getBits }
            }
            .flatten
            .headOption
            .map { bits =>
              FileMode.fromBits(bits)
            }
            .getOrElse(FileMode.REGULAR_FILE)

          val objectId = newPath.map { newPath =>
            val objectId = inserter.insert(Constants.OBJ_BLOB, content)
            builder.add(JGitUtil.createDirCacheEntry(newPath, permission, objectId))
            objectId
          }
          builder.finish()
          objectId
        } else None
    }
  }

  private def _createFiles[R](
    repository: RepositoryService.RepositoryInfo,
    branch: String,
    message: String,
    pusherAccount: Account,
    committerName: String,
    committerMailAddress: String,
    settings: SystemSettings
  )(
    f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => R
  )(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, R)] = {

    LockUtil.lock(s"${repository.owner}/${repository.name}") {
      Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
        val builder = DirCache.newInCore.builder()
        val inserter = git.getRepository.newObjectInserter()
        val headName = s"refs/heads/${branch}"
        val headTip = git.getRepository.resolve(headName)

        val result = f(git, headTip, builder, inserter)

        val commitId = JGitUtil.createNewCommit(
          git,
          inserter,
          headTip,
          builder.getDirCache.writeTree(inserter),
          headName,
          committerName,
          committerMailAddress,
          message
        )

        inserter.flush()
        inserter.close()

        val receivePack = new ReceivePack(git.getRepository)
        val receiveCommand = new ReceiveCommand(headTip, commitId, headName)

        // call pre-commit hook
        val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
          hook.preReceive(repository.owner, repository.name, receivePack, receiveCommand, pusherAccount.userName, false)
        }.headOption

        error match {
          case Some(error) =>
            // commit is rejected
            val refUpdate = git.getRepository.updateRef(headName)
            refUpdate.setNewObjectId(headTip)
            refUpdate.setForceUpdate(true)
            refUpdate.update()
            Left(error)

          case None =>
            // update refs
            val refUpdate = git.getRepository.updateRef(headName)
            refUpdate.setNewObjectId(commitId)
            refUpdate.setForceUpdate(false)
            refUpdate.setRefLogIdent(new PersonIdent(committerName, committerMailAddress))
            refUpdate.update()

            // update pull request
            updatePullRequests(repository.owner, repository.name, branch, pusherAccount, "synchronize", settings)

            // record activity
            updateLastActivityDate(repository.owner, repository.name)
            val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
            val pushInfo = PushInfo(repository.owner, repository.name, pusherAccount.userName, branch, List(commitInfo))
            recordActivity(pushInfo)

            // create issue comment by commit message
            createIssueComment(repository.owner, repository.name, commitInfo)

            // close issue by commit message
            if (branch == repository.repository.defaultBranch) {
              closeIssuesFromMessage(message, committerName, repository.owner, repository.name).foreach { issueId =>
                getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
                  callIssuesWebHook("closed", repository, issue, pusherAccount, settings)
                  val closeIssueInfo = CloseIssueInfo(
                    repository.owner,
                    repository.name,
                    pusherAccount.userName,
                    issue.issueId,
                    issue.title
                  )
                  recordActivity(closeIssueInfo)
                  PluginRegistry().getIssueHooks
                    .foreach(_.closedByCommitComment(issue, repository, message, pusherAccount))
                }
              }
            }

            // call post-commit hook
            PluginRegistry().getReceiveHooks.foreach { hook =>
              hook.postReceive(repository.owner, repository.name, receivePack, receiveCommand, committerName, false)
            }

            val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
            callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) {
              getAccountByUserName(repository.owner).map { ownerAccount =>
                WebHookPushPayload(
                  git,
                  pusherAccount,
                  headName,
                  repository,
                  List(commit),
                  ownerAccount,
                  oldId = headTip,
                  newId = commitId
                )
              }
            }
            Right((commitId, result))
        }
      }
    }
  }

}

object RepositoryCommitFileService {
  case class CommitFile(id: String, name: String)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy