mirror of
				https://github.com/gitbucket/gitbucket.git
				synced 2025-10-31 18:46:28 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			revert_pul
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a791e937b6 | 
| @@ -237,7 +237,7 @@ | |||||||
|   <addForeignKeyConstraint constraintName="IDX_ISSUE_ID_FK1" baseTableName="ISSUE_ID" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/> |   <addForeignKeyConstraint constraintName="IDX_ISSUE_ID_FK1" baseTableName="ISSUE_ID" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/> | ||||||
|  |  | ||||||
|   <!--================================================================================================--> |   <!--================================================================================================--> | ||||||
|   <!-- ISSUE_ID --> |   <!-- ISSUE_LABEL --> | ||||||
|   <!--================================================================================================--> |   <!--================================================================================================--> | ||||||
|   <createTable tableName="ISSUE_LABEL"> |   <createTable tableName="ISSUE_LABEL"> | ||||||
|     <column name="USER_NAME" type="varchar(100)" nullable="false"/> |     <column name="USER_NAME" type="varchar(100)" nullable="false"/> | ||||||
|   | |||||||
| @@ -29,4 +29,12 @@ | |||||||
|   <addPrimaryKey constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_PK" tableName="PROTECTED_BRANCH_RESTRICTION" columnNames="USER_NAME, REPOSITORY_NAME, BRANCH, ALLOWED_USER"/> |   <addPrimaryKey constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_PK" tableName="PROTECTED_BRANCH_RESTRICTION" columnNames="USER_NAME, REPOSITORY_NAME, BRANCH, ALLOWED_USER"/> | ||||||
|   <addForeignKeyConstraint constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_FK0" baseTableName="PROTECTED_BRANCH_RESTRICTION" baseColumnNames="USER_NAME, REPOSITORY_NAME, BRANCH" referencedTableName="PROTECTED_BRANCH" referencedColumnNames="USER_NAME, REPOSITORY_NAME, BRANCH" onDelete="CASCADE" onUpdate="CASCADE"/> |   <addForeignKeyConstraint constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_FK0" baseTableName="PROTECTED_BRANCH_RESTRICTION" baseColumnNames="USER_NAME, REPOSITORY_NAME, BRANCH" referencedTableName="PROTECTED_BRANCH" referencedColumnNames="USER_NAME, REPOSITORY_NAME, BRANCH" onDelete="CASCADE" onUpdate="CASCADE"/> | ||||||
|   <addForeignKeyConstraint constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_FK1" baseTableName="PROTECTED_BRANCH_RESTRICTION" baseColumnNames="ALLOWED_USER" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/> |   <addForeignKeyConstraint constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_FK1" baseTableName="PROTECTED_BRANCH_RESTRICTION" baseColumnNames="ALLOWED_USER" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/> | ||||||
|  |  | ||||||
|  |   <!--================================================================================================--> | ||||||
|  |   <!-- PULL_REQUEST --> | ||||||
|  |   <!--================================================================================================--> | ||||||
|  |   <addColumn tableName="PULL_REQUEST"> | ||||||
|  |     <column name="MERGED_COMMIT_IDS" type="text" nullable="true"/> | ||||||
|  |   </addColumn> | ||||||
|  |  | ||||||
| </changeSet> | </changeSet> | ||||||
|   | |||||||
| @@ -13,8 +13,10 @@ import gitbucket.core.util.Implicits.* | |||||||
| import gitbucket.core.util.* | import gitbucket.core.util.* | ||||||
| import org.scalatra.forms.* | import org.scalatra.forms.* | ||||||
| import org.eclipse.jgit.api.Git | import org.eclipse.jgit.api.Git | ||||||
|  | import org.eclipse.jgit.revwalk.RevWalk | ||||||
| import org.scalatra.BadRequest | import org.scalatra.BadRequest | ||||||
|  |  | ||||||
|  | import java.nio.file.Files | ||||||
| import scala.util.Using | import scala.util.Using | ||||||
|  |  | ||||||
| class PullRequestsController | class PullRequestsController | ||||||
| @@ -247,41 +249,43 @@ trait PullRequestsControllerBase extends ControllerBase { | |||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   get("/:owner/:repository/pull/:id/delete_branch")(readableUsersOnly { baseRepository => |   get("/:owner/:repository/pull/:id/delete_branch")(readableUsersOnly { baseRepository => | ||||||
|     (for { |     context.withLoginAccount { _ => | ||||||
|       issueId <- params("id").toIntOpt |       (for { | ||||||
|       loginAccount <- context.loginAccount |         issueId <- params("id").toIntOpt | ||||||
|       case (issue, pullreq) <- getPullRequest(baseRepository.owner, baseRepository.name, issueId) |         case (issue, pullreq) <- getPullRequest(baseRepository.owner, baseRepository.name, issueId) | ||||||
|       owner = pullreq.requestUserName |         owner = pullreq.requestUserName | ||||||
|       name = pullreq.requestRepositoryName |         name = pullreq.requestRepositoryName | ||||||
|       if hasDeveloperRole(owner, name, context.loginAccount) |         if hasDeveloperRole(owner, name, context.loginAccount) | ||||||
|     } yield { |       } yield { | ||||||
|       val repository = getRepository(owner, name).get |         val repository = getRepository(owner, name).get | ||||||
|       val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch) |         val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch) | ||||||
|       if (branchProtection.enabled) { |         if (branchProtection.enabled) { | ||||||
|         flash.update("error", s"branch ${pullreq.requestBranch} is protected.") |           flash.update("error", s"branch ${pullreq.requestBranch} is protected.") | ||||||
|       } else { |  | ||||||
|         if (repository.repository.defaultBranch != pullreq.requestBranch) { |  | ||||||
|           val userName = context.loginAccount.get.userName |  | ||||||
|           Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => |  | ||||||
|             git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call() |  | ||||||
|             val deleteBranchInfo = DeleteBranchInfo(repository.owner, repository.name, userName, pullreq.requestBranch) |  | ||||||
|             recordActivity(deleteBranchInfo) |  | ||||||
|           } |  | ||||||
|           createComment( |  | ||||||
|             baseRepository.owner, |  | ||||||
|             baseRepository.name, |  | ||||||
|             userName, |  | ||||||
|             issueId, |  | ||||||
|             pullreq.requestBranch, |  | ||||||
|             "delete_branch" |  | ||||||
|           ) |  | ||||||
|         } else { |         } else { | ||||||
|           flash.update("error", s"""Can't delete the default branch "${pullreq.requestBranch}".""") |           if (repository.repository.defaultBranch != pullreq.requestBranch) { | ||||||
|  |             val userName = context.loginAccount.get.userName | ||||||
|  |             Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => | ||||||
|  |               git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call() | ||||||
|  |               val deleteBranchInfo = | ||||||
|  |                 DeleteBranchInfo(repository.owner, repository.name, userName, pullreq.requestBranch) | ||||||
|  |               recordActivity(deleteBranchInfo) | ||||||
|  |             } | ||||||
|  |             createComment( | ||||||
|  |               baseRepository.owner, | ||||||
|  |               baseRepository.name, | ||||||
|  |               userName, | ||||||
|  |               issueId, | ||||||
|  |               pullreq.requestBranch, | ||||||
|  |               "delete_branch" | ||||||
|  |             ) | ||||||
|  |           } else { | ||||||
|  |             flash.update("error", s"""Can't delete the default branch "${pullreq.requestBranch}".""") | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       } |  | ||||||
|  |  | ||||||
|       redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}") |         redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}") | ||||||
|     }) getOrElse NotFound() |       }) getOrElse NotFound() | ||||||
|  |     } | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   post("/:owner/:repository/pull/:id/update_branch")(readableUsersOnly { baseRepository => |   post("/:owner/:repository/pull/:id/update_branch")(readableUsersOnly { baseRepository => | ||||||
| @@ -361,8 +365,11 @@ trait PullRequestsControllerBase extends ControllerBase { | |||||||
|           form.isDraft, |           form.isDraft, | ||||||
|           context.settings |           context.settings | ||||||
|         ) match { |         ) match { | ||||||
|           case Right(objectId) => redirect(s"/${repository.owner}/${repository.name}/pull/$issueId") |           case Right(result) => | ||||||
|           case Left(message)   => Some(BadRequest(message)) |             updateMergedCommitIds(repository.owner, repository.name, issueId, result.mergedCommitId) | ||||||
|  |             redirect(s"/${repository.owner}/${repository.name}/pull/$issueId") | ||||||
|  |           case Left(message) => | ||||||
|  |             Some(BadRequest(message)) | ||||||
|         } |         } | ||||||
|       } getOrElse NotFound() |       } getOrElse NotFound() | ||||||
|     } |     } | ||||||
| @@ -722,6 +729,107 @@ trait PullRequestsControllerBase extends ControllerBase { | |||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   post("/:owner/:repository/pull/:id/revert")(writableUsersOnly { repository => | ||||||
|  |     context.withLoginAccount { loginAccount => | ||||||
|  |       (for { | ||||||
|  |         issueId <- params.get("id").map(_.toInt) | ||||||
|  |         (issue, pullreq) <- getPullRequest(repository.owner, repository.name, issueId) if issue.closed | ||||||
|  |       } yield { | ||||||
|  |         val baseBranch = pullreq.branch | ||||||
|  |         val revertBranch = s"revert-pr-$issueId-${System.currentTimeMillis()}" | ||||||
|  |  | ||||||
|  |         Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => | ||||||
|  |           try { | ||||||
|  |             // Create a new branch from base | ||||||
|  |             JGitUtil.createBranch(git, baseBranch, revertBranch) | ||||||
|  |             // TODO Call webhook ??? | ||||||
|  |  | ||||||
|  |             val tempDir = Files.createTempDirectory("jgit-revert-") | ||||||
|  |             val revertCommitName = | ||||||
|  |               try { | ||||||
|  |                 // Clone bare repository | ||||||
|  |                 Using.resource( | ||||||
|  |                   Git.cloneRepository | ||||||
|  |                     .setURI(getRepositoryDir(repository.owner, repository.name).getAbsolutePath) | ||||||
|  |                     .setDirectory(tempDir.toFile) | ||||||
|  |                     .setBranch(revertBranch) | ||||||
|  |                     .setBare(false) | ||||||
|  |                     .setNoCheckout(false) | ||||||
|  |                     .call() | ||||||
|  |                 ) { git => | ||||||
|  |                   // Get commit Ids to be reverted | ||||||
|  |                   val commitsToRevert = Using.resource(new RevWalk(git.getRepository)) { revWalk => | ||||||
|  |                     pullreq.mergedCommitIds | ||||||
|  |                       .map( | ||||||
|  |                         _.split(",") | ||||||
|  |                           .map { mergedCommitId => | ||||||
|  |                             revWalk.parseCommit(git.getRepository.resolve(mergedCommitId)) | ||||||
|  |                           } | ||||||
|  |                           .toSeq | ||||||
|  |                           .reverse | ||||||
|  |                       ) | ||||||
|  |                       .getOrElse(Nil) | ||||||
|  |                   } | ||||||
|  |  | ||||||
|  |                   // revert | ||||||
|  |                   var revert = git.revert | ||||||
|  |                   commitsToRevert.foreach { id => | ||||||
|  |                     revert = revert.include(id) | ||||||
|  |                   } | ||||||
|  |                   val newCommit = revert.call() | ||||||
|  |                   if (newCommit != null) { | ||||||
|  |                     System.out.println("Reverted commit created: " + newCommit.getName) | ||||||
|  |                     git.push.call() | ||||||
|  |                     Some(newCommit.getName) | ||||||
|  |                   } else { | ||||||
|  |                     System.out.println("Revert resulted in conflicts.") | ||||||
|  |                     None | ||||||
|  |                   } | ||||||
|  |                 } | ||||||
|  |               } finally { | ||||||
|  |                 FileUtil.deleteRecursively(tempDir.toFile) | ||||||
|  |               } | ||||||
|  |  | ||||||
|  |             revertCommitName match { | ||||||
|  |               case Some(revertCommitName) => | ||||||
|  |                 val newIssueId = insertIssue( | ||||||
|  |                   owner = repository.owner, | ||||||
|  |                   repository = repository.name, | ||||||
|  |                   loginUser = loginAccount.userName, | ||||||
|  |                   title = s"Revert #${issueId}", | ||||||
|  |                   content = Some(s"Revert #${issueId}"), | ||||||
|  |                   milestoneId = None, | ||||||
|  |                   priorityId = None, | ||||||
|  |                   isPullRequest = true | ||||||
|  |                 ) | ||||||
|  |                 createPullRequest( | ||||||
|  |                   originRepository = repository, | ||||||
|  |                   issueId = newIssueId, | ||||||
|  |                   originBranch = baseBranch, | ||||||
|  |                   requestUserName = repository.owner, | ||||||
|  |                   requestRepositoryName = repository.name, | ||||||
|  |                   requestBranch = revertBranch, | ||||||
|  |                   commitIdFrom = baseBranch, | ||||||
|  |                   commitIdTo = revertCommitName, | ||||||
|  |                   isDraft = false, | ||||||
|  |                   loginAccount = loginAccount, | ||||||
|  |                   settings = context.settings | ||||||
|  |                 ) | ||||||
|  |                 redirect(s"/${repository.owner}/${repository.name}/pull/$newIssueId") | ||||||
|  |  | ||||||
|  |               case None => | ||||||
|  |                 BadRequest("Failed to create revert commit.") | ||||||
|  |             } | ||||||
|  |           } catch { | ||||||
|  |             case ex: Exception => | ||||||
|  |               ex.printStackTrace() | ||||||
|  |               BadRequest(s"Revert failed: ${ex.getMessage}") | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }) getOrElse NotFound() | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Tests whether an logged-in user can manage pull requests. |    * Tests whether an logged-in user can manage pull requests. | ||||||
|    */ |    */ | ||||||
| @@ -740,5 +848,4 @@ trait PullRequestsControllerBase extends ControllerBase { | |||||||
|       case "DISABLE" => false |       case "DISABLE" => false | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ trait PullRequestComponent extends TemplateComponent { self: Profile => | |||||||
|     val commitIdFrom = column[String]("COMMIT_ID_FROM") |     val commitIdFrom = column[String]("COMMIT_ID_FROM") | ||||||
|     val commitIdTo = column[String]("COMMIT_ID_TO") |     val commitIdTo = column[String]("COMMIT_ID_TO") | ||||||
|     val isDraft = column[Boolean]("IS_DRAFT") |     val isDraft = column[Boolean]("IS_DRAFT") | ||||||
|  |     val mergedCommitIds = column[String]("MERGED_COMMIT_IDS") | ||||||
|     def * = |     def * = | ||||||
|       ( |       ( | ||||||
|         userName, |         userName, | ||||||
| @@ -24,12 +25,13 @@ trait PullRequestComponent extends TemplateComponent { self: Profile => | |||||||
|         requestBranch, |         requestBranch, | ||||||
|         commitIdFrom, |         commitIdFrom, | ||||||
|         commitIdTo, |         commitIdTo, | ||||||
|         isDraft |         isDraft, | ||||||
|  |         mergedCommitIds.? | ||||||
|       ).mapTo[PullRequest] |       ).mapTo[PullRequest] | ||||||
|  |  | ||||||
|     def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) = |     def byPrimaryKey(userName: String, repositoryName: String, issueId: Int): Rep[Boolean] = | ||||||
|       byIssue(userName, repositoryName, issueId) |       byIssue(userName, repositoryName, issueId) | ||||||
|     def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], issueId: Rep[Int]) = |     def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], issueId: Rep[Int]): Rep[Boolean] = | ||||||
|       byIssue(userName, repositoryName, issueId) |       byIssue(userName, repositoryName, issueId) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -44,5 +46,6 @@ case class PullRequest( | |||||||
|   requestBranch: String, |   requestBranch: String, | ||||||
|   commitIdFrom: String, |   commitIdFrom: String, | ||||||
|   commitIdTo: String, |   commitIdTo: String, | ||||||
|   isDraft: Boolean |   isDraft: Boolean, | ||||||
|  |   mergedCommitIds: Option[String] | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import gitbucket.core.plugin.{PluginRegistry, ReceiveHook} | |||||||
| import gitbucket.core.service.RepositoryService.RepositoryInfo | import gitbucket.core.service.RepositoryService.RepositoryInfo | ||||||
| import gitbucket.core.util.Directory._ | import gitbucket.core.util.Directory._ | ||||||
| import gitbucket.core.util.{JGitUtil, LockUtil} | import gitbucket.core.util.{JGitUtil, LockUtil} | ||||||
| import gitbucket.core.model.Profile.profile.blockingApi._ | import gitbucket.core.model.Profile.profile.blockingApi.* | ||||||
| import gitbucket.core.model.activity.{CloseIssueInfo, MergeInfo, PushInfo} | import gitbucket.core.model.activity.{CloseIssueInfo, MergeInfo, PushInfo} | ||||||
| import gitbucket.core.service.SystemSettingsService.SystemSettings | import gitbucket.core.service.SystemSettingsService.SystemSettings | ||||||
| import gitbucket.core.service.WebHookService.WebHookPushPayload | import gitbucket.core.service.WebHookService.WebHookPushPayload | ||||||
| @@ -19,14 +19,14 @@ import org.eclipse.jgit.errors.NoMergeBaseException | |||||||
| import org.eclipse.jgit.lib.{CommitBuilder, ObjectId, PersonIdent, Repository} | import org.eclipse.jgit.lib.{CommitBuilder, ObjectId, PersonIdent, Repository} | ||||||
| import org.eclipse.jgit.revwalk.{RevCommit, RevWalk} | import org.eclipse.jgit.revwalk.{RevCommit, RevWalk} | ||||||
|  |  | ||||||
| import scala.jdk.CollectionConverters._ | import scala.jdk.CollectionConverters.* | ||||||
| import scala.util.Using | import scala.util.Using | ||||||
|  |  | ||||||
| trait MergeService { | trait MergeService { | ||||||
|   self: AccountService & ActivityService & IssuesService & RepositoryService & PullRequestService & |   self: AccountService & ActivityService & IssuesService & RepositoryService & PullRequestService & | ||||||
|     WebHookPullRequestService & WebHookService => |     WebHookPullRequestService & WebHookService => | ||||||
|  |  | ||||||
|   import MergeService._ |   import MergeService.* | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Checks whether conflict will be caused in merging within pull request. |    * Checks whether conflict will be caused in merging within pull request. | ||||||
| @@ -61,15 +61,16 @@ trait MergeService { | |||||||
|     repository: RepositoryInfo, |     repository: RepositoryInfo, | ||||||
|     branch: String, |     branch: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|  |     commits: Seq[RevCommit], | ||||||
|     message: String, |     message: String, | ||||||
|     loginAccount: Account, |     loginAccount: Account, | ||||||
|     settings: SystemSettings |     settings: SystemSettings | ||||||
|   )(implicit s: Session, c: JsonFormat.Context): ObjectId = { |   )(implicit s: Session, c: JsonFormat.Context): MergeResult = { | ||||||
|     val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") |     val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") | ||||||
|     val afterCommitId = new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) |     val mergeResult = new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) | ||||||
|       .merge(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName) |       .merge(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName, commits) | ||||||
|     callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings) |     callWebHook(git, repository, branch, beforeCommitId, mergeResult.newCommitId, loginAccount, settings) | ||||||
|     afterCommitId |     mergeResult | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** rebase to the head of the pull request branch */ |   /** rebase to the head of the pull request branch */ | ||||||
| @@ -81,13 +82,13 @@ trait MergeService { | |||||||
|     commits: Seq[RevCommit], |     commits: Seq[RevCommit], | ||||||
|     loginAccount: Account, |     loginAccount: Account, | ||||||
|     settings: SystemSettings |     settings: SystemSettings | ||||||
|   )(implicit s: Session, c: JsonFormat.Context): ObjectId = { |   )(implicit s: Session, c: JsonFormat.Context): MergeResult = { | ||||||
|     val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") |     val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") | ||||||
|     val afterCommitId = |     val mergeResult = | ||||||
|       new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) |       new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) | ||||||
|         .rebase(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName, commits) |         .rebase(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName, commits) | ||||||
|     callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings) |     callWebHook(git, repository, branch, beforeCommitId, mergeResult.newCommitId, loginAccount, settings) | ||||||
|     afterCommitId |     mergeResult | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** squash commits in the pull request and append it */ |   /** squash commits in the pull request and append it */ | ||||||
| @@ -99,13 +100,13 @@ trait MergeService { | |||||||
|     message: String, |     message: String, | ||||||
|     loginAccount: Account, |     loginAccount: Account, | ||||||
|     settings: SystemSettings |     settings: SystemSettings | ||||||
|   )(implicit s: Session, c: JsonFormat.Context): ObjectId = { |   )(implicit s: Session, c: JsonFormat.Context): MergeResult = { | ||||||
|     val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") |     val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") | ||||||
|     val afterCommitId = |     val mergeResult = | ||||||
|       new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) |       new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) | ||||||
|         .squash(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName) |         .squash(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName) | ||||||
|     callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings) |     callWebHook(git, repository, branch, beforeCommitId, mergeResult.newCommitId, loginAccount, settings) | ||||||
|     afterCommitId |     mergeResult | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private def callWebHook( |   private def callWebHook( | ||||||
| @@ -337,7 +338,7 @@ trait MergeService { | |||||||
|     strategy: String, |     strategy: String, | ||||||
|     isDraft: Boolean, |     isDraft: Boolean, | ||||||
|     settings: SystemSettings |     settings: SystemSettings | ||||||
|   )(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = { |   )(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, MergeResult] = { | ||||||
|     if (!isDraft) { |     if (!isDraft) { | ||||||
|       if (repository.repository.options.mergeOptions.split(",").contains(strategy)) { |       if (repository.repository.options.mergeOptions.split(",").contains(strategy)) { | ||||||
|         LockUtil.lock(s"${repository.owner}/${repository.name}") { |         LockUtil.lock(s"${repository.owner}/${repository.name}") { | ||||||
| @@ -493,7 +494,7 @@ trait MergeService { | |||||||
|     commits: Seq[Seq[CommitInfo]], |     commits: Seq[Seq[CommitInfo]], | ||||||
|     receiveHooks: Seq[ReceiveHook], |     receiveHooks: Seq[ReceiveHook], | ||||||
|     settings: SystemSettings |     settings: SystemSettings | ||||||
|   )(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = { |   )(implicit s: Session, c: JsonFormat.Context): Option[MergeResult] = { | ||||||
|     val revCommits = Using |     val revCommits = Using | ||||||
|       .resource(new RevWalk(git.getRepository)) { revWalk => |       .resource(new RevWalk(git.getRepository)) { revWalk => | ||||||
|         commits.flatten.map { commit => |         commits.flatten.map { commit => | ||||||
| @@ -510,6 +511,7 @@ trait MergeService { | |||||||
|             repository, |             repository, | ||||||
|             pullRequest.branch, |             pullRequest.branch, | ||||||
|             issue.issueId, |             issue.issueId, | ||||||
|  |             revCommits, | ||||||
|             s"Merge pull request #${issue.issueId} from ${pullRequest.requestUserName}/${pullRequest.requestBranch}\n\n" + message, |             s"Merge pull request #${issue.issueId} from ${pullRequest.requestUserName}/${pullRequest.requestBranch}\n\n" + message, | ||||||
|             loginAccount, |             loginAccount, | ||||||
|             settings |             settings | ||||||
| @@ -600,13 +602,13 @@ object MergeService { | |||||||
|     private val mergedBranchName = s"refs/pull/${issueId}/merge" |     private val mergedBranchName = s"refs/pull/${issueId}/merge" | ||||||
|     private val conflictedBranchName = s"refs/pull/${issueId}/conflict" |     private val conflictedBranchName = s"refs/pull/${issueId}/conflict" | ||||||
|  |  | ||||||
|     lazy val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${branch}") |     lazy val mergeBaseTip: ObjectId = git.getRepository.resolve(s"refs/heads/${branch}") | ||||||
|     lazy val mergeTip = git.getRepository.resolve(s"refs/pull/${issueId}/head") |     lazy val mergeTip: ObjectId = git.getRepository.resolve(s"refs/pull/${issueId}/head") | ||||||
|  |  | ||||||
|     def checkConflictCache(): Option[Option[String]] = { |     def checkConflictCache(): Option[Option[String]] = { | ||||||
|       Option(git.getRepository.resolve(mergedBranchName)) |       Option(git.getRepository.resolve(mergedBranchName)) | ||||||
|         .flatMap { merged => |         .flatMap { merged => | ||||||
|           if (parseCommit(merged).getParents().toSet == Set(mergeBaseTip, mergeTip)) { |           if (parseCommit(merged).getParents.toSet == Set(mergeBaseTip, mergeTip)) { | ||||||
|             // merged branch exists |             // merged branch exists | ||||||
|             Some(None) |             Some(None) | ||||||
|           } else { |           } else { | ||||||
| @@ -615,7 +617,7 @@ object MergeService { | |||||||
|         } |         } | ||||||
|         .orElse(Option(git.getRepository.resolve(conflictedBranchName)).flatMap { conflicted => |         .orElse(Option(git.getRepository.resolve(conflictedBranchName)).flatMap { conflicted => | ||||||
|           val commit = parseCommit(conflicted) |           val commit = parseCommit(conflicted) | ||||||
|           if (commit.getParents().toSet == Set(mergeBaseTip, mergeTip)) { |           if (commit.getParents.toSet == Set(mergeBaseTip, mergeTip)) { | ||||||
|             // conflict branch exists |             // conflict branch exists | ||||||
|             Some(Some(commit.getFullMessage)) |             Some(Some(commit.getFullMessage)) | ||||||
|           } else { |           } else { | ||||||
| @@ -651,14 +653,16 @@ object MergeService { | |||||||
|         None |         None | ||||||
|       } else { |       } else { | ||||||
|         val message = createConflictMessage(mergeTip, mergeBaseTip, merger) |         val message = createConflictMessage(mergeTip, mergeBaseTip, merger) | ||||||
|         _updateBranch(mergeTipCommit.getTree().getId(), message, conflictedBranchName) |         _updateBranch(mergeTipCommit.getTree.getId, message, conflictedBranchName) | ||||||
|         git.branchDelete().setForce(true).setBranchNames(mergedBranchName).call() |         git.branchDelete().setForce(true).setBranchNames(mergedBranchName).call() | ||||||
|         Some(message) |         Some(message) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // update branch from cache |     // update branch from cache | ||||||
|     def merge(message: String, committer: PersonIdent, pusher: String)(implicit s: Session): ObjectId = { |     def merge(message: String, committer: PersonIdent, pusher: String, commits: Seq[RevCommit])(implicit | ||||||
|  |       s: Session | ||||||
|  |     ): MergeResult = { | ||||||
|       if (checkConflict().isDefined) { |       if (checkConflict().isDefined) { | ||||||
|         throw new RuntimeException("This pull request can't merge automatically.") |         throw new RuntimeException("This pull request can't merge automatically.") | ||||||
|       } |       } | ||||||
| @@ -666,7 +670,7 @@ object MergeService { | |||||||
|         throw new RuntimeException(s"Not found branch ${mergedBranchName}") |         throw new RuntimeException(s"Not found branch ${mergedBranchName}") | ||||||
|       }) |       }) | ||||||
|       // creates merge commit |       // creates merge commit | ||||||
|       val mergeCommitId = createMergeCommit(mergeResultCommit.getTree().getId(), committer, message) |       val mergeCommitId = createMergeCommit(mergeResultCommit.getTree.getId, committer, message) | ||||||
|  |  | ||||||
|       val refName = s"refs/heads/${branch}" |       val refName = s"refs/heads/${branch}" | ||||||
|       val currentObjectId = git.getRepository.resolve(refName) |       val currentObjectId = git.getRepository.resolve(refName) | ||||||
| @@ -690,10 +694,10 @@ object MergeService { | |||||||
|         hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) |         hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       objectId |       MergeResult(objectId, commits.map(_.name())) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     def rebase(committer: PersonIdent, pusher: String, commits: Seq[RevCommit])(implicit s: Session): ObjectId = { |     def rebase(committer: PersonIdent, pusher: String, commits: Seq[RevCommit])(implicit s: Session): MergeResult = { | ||||||
|       if (checkConflict().isDefined) { |       if (checkConflict().isDefined) { | ||||||
|         throw new RuntimeException("This pull request can't merge automatically.") |         throw new RuntimeException("This pull request can't merge automatically.") | ||||||
|       } |       } | ||||||
| @@ -713,11 +717,13 @@ object MergeService { | |||||||
|  |  | ||||||
|       val mergeBaseTipCommit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(mergeBaseTip)) |       val mergeBaseTipCommit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(mergeBaseTip)) | ||||||
|       var previousId = mergeBaseTipCommit.getId |       var previousId = mergeBaseTipCommit.getId | ||||||
|  |       val mergedCommitIds = Seq.newBuilder[String] | ||||||
|  |  | ||||||
|       Using.resource(git.getRepository.newObjectInserter) { inserter => |       Using.resource(git.getRepository.newObjectInserter) { inserter => | ||||||
|         commits.foreach { commit => |         commits.foreach { commit => | ||||||
|           val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId) |           val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId) | ||||||
|           previousId = inserter.insert(nextCommit) |           previousId = inserter.insert(nextCommit) | ||||||
|  |           mergedCommitIds += previousId.name() | ||||||
|         } |         } | ||||||
|         inserter.flush() |         inserter.flush() | ||||||
|       } |       } | ||||||
| @@ -745,10 +751,10 @@ object MergeService { | |||||||
|         hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) |         hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       objectId |       MergeResult(objectId, mergedCommitIds.result()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     def squash(message: String, committer: PersonIdent, pusher: String)(implicit s: Session): ObjectId = { |     def squash(message: String, committer: PersonIdent, pusher: String)(implicit s: Session): MergeResult = { | ||||||
|       if (checkConflict().isDefined) { |       if (checkConflict().isDefined) { | ||||||
|         throw new RuntimeException("This pull request can't merge automatically.") |         throw new RuntimeException("This pull request can't merge automatically.") | ||||||
|       } |       } | ||||||
| @@ -804,7 +810,7 @@ object MergeService { | |||||||
|         hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) |         hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       objectId |       MergeResult(objectId, Seq(newCommitId.name())) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // return treeId |     // return treeId | ||||||
| @@ -823,4 +829,5 @@ object MergeService { | |||||||
|       mergeResults.asScala.map { case (key, _) => "- `" + key + "`\n" }.mkString |       mergeResults.asScala.map { case (key, _) => "- `" + key + "`\n" }.mkString | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   case class MergeResult(newCommitId: ObjectId, mergedCommitId: Seq[String]) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -63,6 +63,15 @@ trait PullRequestService { | |||||||
|       .update((baseBranch, commitIdTo)) |       .update((baseBranch, commitIdTo)) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   def updateMergedCommitIds(owner: String, repository: String, issueId: Int, mergedCommitIds: Seq[String])(implicit | ||||||
|  |     s: Session | ||||||
|  |   ): Unit = { | ||||||
|  |     PullRequests | ||||||
|  |       .filter(_.byPrimaryKey(owner, repository, issueId)) | ||||||
|  |       .map(pr => pr.mergedCommitIds) | ||||||
|  |       .update(mergedCommitIds.mkString(",")) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(implicit |   def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(implicit | ||||||
|     s: Session |     s: Session | ||||||
|   ): List[PullRequestCount] = |   ): List[PullRequestCount] = | ||||||
| @@ -126,7 +135,8 @@ trait PullRequestService { | |||||||
|         requestBranch, |         requestBranch, | ||||||
|         commitIdFrom, |         commitIdFrom, | ||||||
|         commitIdTo, |         commitIdTo, | ||||||
|         isDraft |         isDraft, | ||||||
|  |         None | ||||||
|       ) |       ) | ||||||
|  |  | ||||||
|       // fetch requested branch |       // fetch requested branch | ||||||
| @@ -408,11 +418,10 @@ trait PullRequestService { | |||||||
|         .find(x => x.oldPath == file) |         .find(x => x.oldPath == file) | ||||||
|         .map { diff => |         .map { diff => | ||||||
|           (diff.oldContent, diff.newContent) match { |           (diff.oldContent, diff.newContent) match { | ||||||
|             case (Some(oldContent), Some(newContent)) => { |             case (Some(oldContent), Some(newContent)) => | ||||||
|               val oldLines = convertLineSeparator(oldContent, "LF").split("\n") |               val oldLines = convertLineSeparator(oldContent, "LF").split("\n") | ||||||
|               val newLines = convertLineSeparator(newContent, "LF").split("\n") |               val newLines = convertLineSeparator(newContent, "LF").split("\n") | ||||||
|               file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava)) |               file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava)) | ||||||
|             } |  | ||||||
|             case _ => |             case _ => | ||||||
|               file -> None |               file -> None | ||||||
|           } |           } | ||||||
|   | |||||||
| @@ -81,4 +81,13 @@ | |||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|   </script> |   </script> | ||||||
|  |   @if(issue.closed && pullreq.mergedCommitIds.isDefined){ | ||||||
|  |     <div class="issue-comment-box" style="margin-bottom: 20px;"> | ||||||
|  |       <div style="padding: 10px;"> | ||||||
|  |         <form method="post" action="@helpers.url(repository)/pull/@issue.issueId/revert" style="display:inline;"> | ||||||
|  |           <button type="submit" class="btn btn-warning pull-right" style="margin-left: 10px;">Revert</button> | ||||||
|  |         </form> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -154,7 +154,8 @@ object ApiSpecModels { | |||||||
|     requestBranch = "new-topic", |     requestBranch = "new-topic", | ||||||
|     commitIdFrom = sha1, |     commitIdFrom = sha1, | ||||||
|     commitIdTo = sha1, |     commitIdTo = sha1, | ||||||
|     isDraft = true |     isDraft = true, | ||||||
|  |     mergedCommitIds = None | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   val commitComment: CommitComment = CommitComment( |   val commitComment: CommitComment = CommitComment( | ||||||
|   | |||||||
| @@ -171,6 +171,7 @@ class MergeServiceSpec extends AnyFunSpec with ServiceSpecBase { | |||||||
|               repository, |               repository, | ||||||
|               branch, |               branch, | ||||||
|               issueId, |               issueId, | ||||||
|  |               Seq(git.getRepository.parseCommit(commitId)), | ||||||
|               "merged", |               "merged", | ||||||
|               context.loginAccount.get, |               context.loginAccount.get, | ||||||
|               context.settings |               context.settings | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user