Compare commits

..

46 Commits

Author SHA1 Message Date
Naoki Takezoe
a79142074f Fix community plugins link in README (#3867)
Updated the link to the GitBucket community plugins page.
2025-11-01 15:53:22 +09:00
Scala Steward
9f58c6dce7 Update sbt-scalafmt to 2.5.6 2025-10-31 07:27:37 +09:00
dependabot[bot]
6fe903afab Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 13:23:19 +09:00
Scala Steward
0d94865633 Update sbt-scoverage to 2.4.0 2025-10-22 08:07:20 +09:00
xuwei-k
2fbdead64b Update testcontainers 2025-10-20 07:59:52 +09:00
Scala Steward
72a354931e Update scalafmt-core to 3.10.1 2025-10-20 07:28:21 +09:00
Scala Steward
cb8affcd0d Update logback-classic to 1.5.20 2025-10-20 06:53:52 +09:00
scala-steward-bot
a5a997eb40 Update scalafmt-core to 3.10.0 (#3857) 2025-10-18 10:47:07 +09:00
Naoki Takezoe
b4220aab68 Test with MySQL 8.4 (#3860) 2025-10-18 10:40:16 +09:00
Scala Steward
c8a4798f86 Update oauth2-oidc-sdk to 11.30 2025-10-16 20:05:41 +09:00
xuwei-k
6b8e9e8892 add JDK 25 CI 2025-10-13 17:57:35 +09:00
xuwei-k
9ab9363d0b fix warnings 2025-10-13 17:56:55 +09:00
Scala Steward
1edb18a147 Update sbt, sbt-dependency-tree, ... to 1.11.7 2025-10-06 06:59:52 +09:00
Scala Steward
d8b4bf3033 Update thumbnailator to 0.4.21 2025-10-03 06:32:36 +09:00
Scala Steward
6597c4490b Update logback-classic to 1.5.19 2025-10-01 06:22:46 +09:00
Scala Steward
daaf1696ad Update oauth2-oidc-sdk to 11.29.2 2025-09-30 21:44:47 +09:00
Scala Steward
53a1ca7874 Update scala-library to 2.13.17 2025-09-30 18:43:32 +09:00
Scala Steward
f045fa4c6a Update h2 to 2.4.240 2025-09-25 06:58:37 +09:00
Scala Steward
53a7c5adf8 Update sbt-license-report to 1.9.0 2025-09-25 04:25:34 +09:00
takezoe
b142eca9a5 Update change log for 4.44.0 2025-09-23 09:56:19 +09:00
Naoki Takezoe
ba753a373b Fix release doc (#3844) 2025-09-23 09:46:49 +09:00
Naoki Takezoe
95ceca75a4 Update change log for 4.44.0 (#3845) 2025-09-23 09:46:35 +09:00
Naoki Takezoe
f16cc117a9 Release 4.44.0 (#3842) 2025-09-23 09:02:24 +09:00
Naoki Takezoe
af66f8f746 Fix Repository Contents Upload API for nested path (#3843) 2025-09-23 09:01:30 +09:00
Scala Steward
d9cc57e8e0 Update oauth2-oidc-sdk to 11.29.1 2025-09-23 06:51:06 +09:00
Scala Steward
72b6dad3a2 Update mockito-core to 5.20.0 2025-09-21 06:10:09 +09:00
Scala Steward
18f396b4a2 Update postgresql to 42.7.8 2025-09-19 13:46:36 +09:00
Scala Steward
9f3fde8de2 Update tika-core to 3.2.3 2025-09-17 07:14:08 +09:00
scala-steward-bot
dfd6f80b63 Update scalafmt-core to 3.9.10 (#3838) 2025-09-16 22:15:57 +09:00
Scala Steward
119d91210c Update typesafe:config to 1.4.5 2025-09-11 17:29:06 +09:00
Scala Steward
75ef30ee03 Update sbt-license-report to 1.8.0 2025-09-11 06:32:21 +09:00
Scala Steward
d1cf9dd600 Update scala3-library to 3.7.3 2025-09-09 06:18:29 +09:00
Scala Steward
9c9fea908c Update sbt, sbt-dependency-tree, ... to 1.11.6 2025-09-07 08:51:05 +09:00
kenji yoshida
1145c4d0f6 update build.sbt. prepare sbt 2 2025-09-05 20:03:01 +09:00
Scala Steward
d847fc6e0f Update github-api to 1.330 2025-09-05 05:36:45 +09:00
Scala Steward
bb9585f7a6 Update oauth2-oidc-sdk to 11.28 2025-08-31 06:58:03 +09:00
Scala Steward
e91411fa45 Update sbt, sbt-dependency-tree, ... to 1.11.5 2025-08-26 08:30:43 +09:00
dependabot[bot]
adbc065a6f Bump actions/setup-java from 4 to 5
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-25 17:59:37 +09:00
Scala Steward
862283b729 Update apache-sshd to 2.16.0 2025-08-24 06:44:33 +09:00
Naoki Takezoe
217df7012c Fix Wiki branch resolution in blob endpoint (#3826) 2025-08-23 17:11:11 +09:00
Naoki Takezoe
e672d41e77 Fix downloading branch that contains slash (#3825) 2025-08-23 16:49:18 +09:00
Scala Steward
d975700bd4 Update HikariCP to 7.0.2 2025-08-20 03:24:57 +09:00
dependabot[bot]
f65e41561a Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-18 17:51:52 +09:00
Scala Steward
dab4f33ed9 Update oauth2-oidc-sdk to 11.27.1 2025-08-16 12:30:05 +09:00
Scala Steward
5ff45ef5ae Update jetty-http, jetty-io, jetty-runner, ... to 10.0.26 2025-08-16 06:35:41 +09:00
Scala Steward
af7c622647 Update mockito-core to 5.19.0 2025-08-15 18:58:23 +09:00
25 changed files with 161 additions and 305 deletions

View File

@@ -9,9 +9,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
java: [17, 21] java: [17, 25]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Cache - name: Cache
uses: actions/cache@v4 uses: actions/cache@v4
env: env:
@@ -23,7 +23,7 @@ jobs:
~/.cache/coursier/v1 ~/.cache/coursier/v1
key: build-${{ env.cache-name }}-${{ hashFiles('build.sbt') }} key: build-${{ env.cache-name }}-${{ hashFiles('build.sbt') }}
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
distribution: adopt distribution: adopt
@@ -36,7 +36,7 @@ jobs:
- name: Build executable - name: Build executable
run: sbt executable run: sbt executable
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: gitbucket-java${{ matrix.java }}-${{ github.sha }} name: gitbucket-java${{ matrix.java }}-${{ github.sha }}
path: ./target/executable/gitbucket.* path: ./target/executable/gitbucket.*

View File

@@ -1,4 +1,4 @@
version = "3.9.9" version = "3.10.1"
project.git = true project.git = true
maxColumn = 120 maxColumn = 120

View File

@@ -1,6 +1,12 @@
# Changelog # Changelog
All changes to the project will be documented in this file. All changes to the project will be documented in this file.
## 4.44.0 - 23 Sep 2025
- Enhanced branch protection which supports the following settings:
- Prevent pushes from non-allowed users
- Whether to apply restrictions to administrator users as well
- Improve logging for initialization errors
## 4.43.0 - 29 Jun 2025 ## 4.43.0 - 29 Jun 2025
- Upgrade H2 database from 1.x to 2.x - Upgrade H2 database from 1.x to 2.x

View File

@@ -44,7 +44,7 @@ GitBucket has a plug-in system that allows extra functionality. Officially the f
- [gitbucket-pages-plugin](https://github.com/gitbucket/gitbucket-pages-plugin) - [gitbucket-pages-plugin](https://github.com/gitbucket/gitbucket-pages-plugin)
- [gitbucket-notifications-plugin](https://github.com/gitbucket/gitbucket-notifications-plugin) - [gitbucket-notifications-plugin](https://github.com/gitbucket/gitbucket-notifications-plugin)
You can find more plugins made by the community at [GitBucket community plugins](https://gitbucket-plugins.github.io/). You can find more plugins made by the community at [GitBucket community plugins](https://github.com/gitbucket/gitbucket/wiki/Community-Plugins).
Building and Development Building and Development
----------- -----------
@@ -59,12 +59,15 @@ Support
- If you can't find same question and report, send it to our [Gitter chat room](https://gitter.im/gitbucket/gitbucket) before raising an issue. - If you can't find same question and report, send it to our [Gitter chat room](https://gitter.im/gitbucket/gitbucket) before raising an issue.
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles. - The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles.
What's New in 4.43.x What's New in 4.44.x
------------- -------------
## 4.43.0 - 29 Jun 2025 ## 4.44.0 - 23 Sep 2025
- Upgrade H2 database from 1.x to 2.x - Enhanced branch protection which supports the following settings:
- Prevent pushes from non-allowed users
- Whether to apply restrictions to administrator users as well
- Improve logging for initialization errors
Note that upgrading from h2 1.x to 2.x requires data file migration: https://www.h2database.com/html/migration-to-v2.html Note that you have to migrate h2 database file if you will upgrade GitBucket from 4.42 or before to 4.43 or later and you are using the default h2 database because h2 1.x and h2.x don't have compatibility: https://www.h2database.com/html/migration-to-v2.html
It can't be done automatically using GitBucket's auto migration mechanism because it relies on database itself. So, users who use h2 will have to dump and recreate their database manually with the following steps: It can't be done automatically using GitBucket's auto migration mechanism because it relies on database itself. So, users who use h2 will have to dump and recreate their database manually with the following steps:
```bash ```bash

View File

@@ -2,9 +2,9 @@ import com.jsuereth.sbtpgp.PgpKeys._
val Organization = "io.github.gitbucket" val Organization = "io.github.gitbucket"
val Name = "gitbucket" val Name = "gitbucket"
val GitBucketVersion = "4.43.0" val GitBucketVersion = "4.44.0"
val ScalatraVersion = "3.1.2" val ScalatraVersion = "3.1.2"
val JettyVersion = "10.0.25" val JettyVersion = "10.0.26"
val JgitVersion = "6.10.1.202505221210-r" val JgitVersion = "6.10.1.202505221210-r"
lazy val root = (project in file(".")) lazy val root = (project in file("."))
@@ -14,9 +14,9 @@ sourcesInBase := false
organization := Organization organization := Organization
name := Name name := Name
version := GitBucketVersion version := GitBucketVersion
scalaVersion := "2.13.16" scalaVersion := "2.13.17"
crossScalaVersions += "3.7.2" crossScalaVersions += "3.7.3"
// scalafmtOnCompile := true // scalafmtOnCompile := true
@@ -37,38 +37,37 @@ libraryDependencies ++= Seq(
"org.apache.commons" % "commons-email" % "1.6.0", "org.apache.commons" % "commons-email" % "1.6.0",
"commons-net" % "commons-net" % "3.12.0", "commons-net" % "commons-net" % "3.12.0",
"org.apache.httpcomponents" % "httpclient" % "4.5.14", "org.apache.httpcomponents" % "httpclient" % "4.5.14",
"org.apache.sshd" % "apache-sshd" % "2.15.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ( "org.apache.sshd" % "apache-sshd" % "2.16.0" exclude ("org.slf4j", "slf4j-jdk14") exclude (
"org.apache.sshd", "org.apache.sshd",
"sshd-mina" "sshd-mina"
) exclude ("org.apache.sshd", "sshd-netty") ) exclude ("org.apache.sshd", "sshd-netty")
exclude ("org.apache.sshd", "sshd-spring-sftp"), exclude ("org.apache.sshd", "sshd-spring-sftp"),
"org.apache.tika" % "tika-core" % "3.2.2", "org.apache.tika" % "tika-core" % "3.2.3",
"com.github.takezoe" %% "blocking-slick" % "0.0.14", "com.github.takezoe" %% "blocking-slick" % "0.0.14",
"com.novell.ldap" % "jldap" % "2009-10-07", "com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "2.3.232", "com.h2database" % "h2" % "2.4.240",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12", "org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12",
"org.postgresql" % "postgresql" % "42.7.7", "org.postgresql" % "postgresql" % "42.7.8",
"ch.qos.logback" % "logback-classic" % "1.5.18", "ch.qos.logback" % "logback-classic" % "1.5.20",
"com.zaxxer" % "HikariCP" % "7.0.1" exclude ("org.slf4j", "slf4j-api"), "com.zaxxer" % "HikariCP" % "7.0.2" exclude ("org.slf4j", "slf4j-api"),
"com.typesafe" % "config" % "1.4.4", "com.typesafe" % "config" % "1.4.5",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0", "fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
"io.github.java-diff-utils" % "java-diff-utils" % "4.16", "io.github.java-diff-utils" % "java-diff-utils" % "4.16",
"org.cache2k" % "cache2k-all" % "1.6.0.Final", "org.cache2k" % "cache2k-all" % "1.6.0.Final",
"net.coobird" % "thumbnailator" % "0.4.20", "net.coobird" % "thumbnailator" % "0.4.21",
"com.github.zafarkhaja" % "java-semver" % "0.10.2", "com.github.zafarkhaja" % "java-semver" % "0.10.2",
"com.nimbusds" % "oauth2-oidc-sdk" % "11.27", "com.nimbusds" % "oauth2-oidc-sdk" % "11.30",
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided", "org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.13.2" % "test", "junit" % "junit" % "4.13.2" % "test",
"org.scalatra" %% "scalatra-scalatest-javax" % ScalatraVersion % "test", "org.scalatra" %% "scalatra-scalatest-javax" % ScalatraVersion % "test",
"org.mockito" % "mockito-core" % "5.18.0" % "test", "org.mockito" % "mockito-core" % "5.20.0" % "test",
"com.dimafeng" %% "testcontainers-scala" % "0.43.0" % "test", "org.testcontainers" % "testcontainers-mysql" % "2.0.1" % "test",
"org.testcontainers" % "mysql" % "1.21.3" % "test", "org.testcontainers" % "testcontainers-postgresql" % "2.0.1" % "test",
"org.testcontainers" % "postgresql" % "1.21.3" % "test", "net.i2p.crypto" % "eddsa" % "0.3.0",
"net.i2p.crypto" % "eddsa" % "0.3.0", "is.tagomor.woothee" % "woothee-java" % "1.11.0",
"is.tagomor.woothee" % "woothee-java" % "1.11.0", "org.ec4j.core" % "ec4j-core" % "1.1.1",
"org.ec4j.core" % "ec4j-core" % "1.1.1", "org.kohsuke" % "github-api" % "1.330" % "test"
"org.kohsuke" % "github-api" % "1.329" % "test"
) )
// Compiler settings // Compiler settings
@@ -192,7 +191,7 @@ executableKey := {
// zip it up // zip it up
IO delete (temp / "META-INF" / "MANIFEST.MF") IO delete (temp / "META-INF" / "MANIFEST.MF")
val contentMappings = (temp.allPaths --- PathFinder(temp)).get pair { file => val contentMappings = (temp.allPaths --- PathFinder(temp)).get() pair { file =>
IO.relativizeFile(temp, file) IO.relativizeFile(temp, file)
} }
val manifest = new JarManifest val manifest = new JarManifest

View File

@@ -38,7 +38,7 @@ Generate release files
For plug-in development, we have to publish the GitBucket jar file to the Maven central repository before release GitBucket itself. For plug-in development, we have to publish the GitBucket jar file to the Maven central repository before release GitBucket itself.
First, start the sbt shell: First, stage artifacts on your machine:
```bash ```bash
$ sbt publishSigned $ sbt publishSigned

View File

@@ -1 +1 @@
sbt.version=1.11.4 sbt.version=1.11.7

View File

@@ -1,11 +1,11 @@
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature") scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.5") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.6")
addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.9") addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.9")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4") addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1")
addSbtPlugin("com.github.sbt" % "sbt-license-report" % "1.7.0") addSbtPlugin("com.github.sbt" % "sbt-license-report" % "1.9.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.4.0")
addDependencyTreePlugin addDependencyTreePlugin

View File

@@ -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_LABEL --> <!-- ISSUE_ID -->
<!--================================================================================================--> <!--================================================================================================-->
<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"/>

View File

@@ -29,12 +29,4 @@
<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>

View File

@@ -351,7 +351,7 @@ case class Context(
val path: String = settings.baseUrl.getOrElse(request.getContextPath) val path: String = settings.baseUrl.getOrElse(request.getContextPath)
val currentPath: String = request.getRequestURI.substring(request.getContextPath.length) val currentPath: String = request.getRequestURI.substring(request.getContextPath.length)
val baseUrl: String = settings.baseUrl(request) val baseUrl: String = settings.baseUrl(request)
val host: String = new java.net.URL(baseUrl).getHost val host: String = new java.net.URI(baseUrl).toURL.getHost
val platform: String = request.getHeader("User-Agent") match { val platform: String = request.getHeader("User-Agent") match {
case null => null case null => null
case agent if agent.contains("Mac") => "mac" case agent if agent.contains("Mac") => "mac"

View File

@@ -13,10 +13,8 @@ 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
@@ -249,43 +247,41 @@ trait PullRequestsControllerBase extends ControllerBase {
}) })
get("/:owner/:repository/pull/:id/delete_branch")(readableUsersOnly { baseRepository => get("/:owner/:repository/pull/:id/delete_branch")(readableUsersOnly { baseRepository =>
context.withLoginAccount { _ => (for {
(for { issueId <- params("id").toIntOpt
issueId <- params("id").toIntOpt loginAccount <- context.loginAccount
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 { } else {
if (repository.repository.defaultBranch != pullreq.requestBranch) { if (repository.repository.defaultBranch != pullreq.requestBranch) {
val userName = context.loginAccount.get.userName val userName = context.loginAccount.get.userName
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call() git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call()
val deleteBranchInfo = val deleteBranchInfo = DeleteBranchInfo(repository.owner, repository.name, userName, pullreq.requestBranch)
DeleteBranchInfo(repository.owner, repository.name, userName, pullreq.requestBranch) recordActivity(deleteBranchInfo)
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}".""")
} }
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 =>
@@ -365,11 +361,8 @@ trait PullRequestsControllerBase extends ControllerBase {
form.isDraft, form.isDraft,
context.settings context.settings
) match { ) match {
case Right(result) => case Right(objectId) => redirect(s"/${repository.owner}/${repository.name}/pull/$issueId")
updateMergedCommitIds(repository.owner, repository.name, issueId, result.mergedCommitId) case Left(message) => Some(BadRequest(message))
redirect(s"/${repository.owner}/${repository.name}/pull/$issueId")
case Left(message) =>
Some(BadRequest(message))
} }
} getOrElse NotFound() } getOrElse NotFound()
} }
@@ -729,107 +722,6 @@ 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.
*/ */
@@ -848,4 +740,5 @@ trait PullRequestsControllerBase extends ControllerBase {
case "DISABLE" => false case "DISABLE" => false
} }
} }
} }

View File

@@ -1076,14 +1076,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
redirect(s"${repository.owner}/${repository.name}/releases") redirect(s"${repository.owner}/${repository.name}/releases")
}) })
get("/:owner/:repository/archive/:name")(referrersOnly { repository => get("/:owner/:repository/archive/*")(referrersOnly { repository =>
val name = params("name") val name = multiParams("splat").mkString("/")
archiveRepository(name, repository, "") val path = params.get("path").getOrElse("")
})
get("/:owner/:repository/archive/*/:name")(referrersOnly { repository =>
val name = params("name")
val path = multiParams("splat").head
archiveRepository(name, repository, path) archiveRepository(name, repository, path)
}) })

View File

@@ -306,7 +306,8 @@ trait WikiControllerBase extends ControllerBase {
get("/:owner/:repository/wiki/_history")(referrersOnly { repository => get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git => Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
JGitUtil.getCommitLog(git, "master") match { val branch = getWikiBranch(repository.owner, repository.name)
JGitUtil.getCommitLog(git, branch) match {
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository)) case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
case Left(_) => NotFound() case Left(_) => NotFound()
} }
@@ -316,7 +317,8 @@ trait WikiControllerBase extends ControllerBase {
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository => get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
val path = multiParams("splat").head val path = multiParams("splat").head
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git => Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master")) val branch = getWikiBranch(repository.owner, repository.name)
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
getPathObjectId(git, path, revCommit).map { objectId => getPathObjectId(git, path, revCommit).map { objectId =>
responseRawFile(git, objectId, path, repository) responseRawFile(git, objectId, path, repository)

View File

@@ -142,10 +142,11 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch)) val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
revCommit.name revCommit.name
} }
val paths = multiParams("splat").head.split("/") val fullPath = multiParams("splat").head
val paths = fullPath.split("/")
val path = paths.take(paths.size - 1).toList.mkString("/") val path = paths.take(paths.size - 1).toList.mkString("/")
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git => Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
val fileInfo = getFileInfo(git, commit, path, false) val fileInfo = getFileInfo(git, commit, fullPath, ignoreCase = false)
fileInfo match { fileInfo match {
case Some(f) if !data.sha.contains(f.id.getName) => case Some(f) if !data.sha.contains(f.id.getName) =>

View File

@@ -13,7 +13,6 @@ 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,
@@ -25,13 +24,12 @@ 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): Rep[Boolean] = def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
byIssue(userName, repositoryName, issueId) byIssue(userName, repositoryName, issueId)
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], issueId: Rep[Int]): Rep[Boolean] = def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], issueId: Rep[Int]) =
byIssue(userName, repositoryName, issueId) byIssue(userName, repositoryName, issueId)
} }
} }
@@ -46,6 +44,5 @@ case class PullRequest(
requestBranch: String, requestBranch: String,
commitIdFrom: String, commitIdFrom: String,
commitIdTo: String, commitIdTo: String,
isDraft: Boolean, isDraft: Boolean
mergedCommitIds: Option[String]
) )

View File

@@ -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,16 +61,15 @@ 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): MergeResult = { )(implicit s: Session, c: JsonFormat.Context): ObjectId = {
val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}")
val mergeResult = new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks()) val afterCommitId = new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks())
.merge(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName, commits) .merge(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), loginAccount.userName)
callWebHook(git, repository, branch, beforeCommitId, mergeResult.newCommitId, loginAccount, settings) callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings)
mergeResult afterCommitId
} }
/** rebase to the head of the pull request branch */ /** rebase to the head of the pull request branch */
@@ -82,13 +81,13 @@ trait MergeService {
commits: Seq[RevCommit], commits: Seq[RevCommit],
loginAccount: Account, loginAccount: Account,
settings: SystemSettings settings: SystemSettings
)(implicit s: Session, c: JsonFormat.Context): MergeResult = { )(implicit s: Session, c: JsonFormat.Context): ObjectId = {
val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}")
val mergeResult = val afterCommitId =
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, mergeResult.newCommitId, loginAccount, settings) callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings)
mergeResult afterCommitId
} }
/** squash commits in the pull request and append it */ /** squash commits in the pull request and append it */
@@ -100,13 +99,13 @@ trait MergeService {
message: String, message: String,
loginAccount: Account, loginAccount: Account,
settings: SystemSettings settings: SystemSettings
)(implicit s: Session, c: JsonFormat.Context): MergeResult = { )(implicit s: Session, c: JsonFormat.Context): ObjectId = {
val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}") val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}")
val mergeResult = val afterCommitId =
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, mergeResult.newCommitId, loginAccount, settings) callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings)
mergeResult afterCommitId
} }
private def callWebHook( private def callWebHook(
@@ -338,7 +337,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, MergeResult] = { )(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
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}") {
@@ -494,7 +493,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[MergeResult] = { )(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
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 =>
@@ -511,7 +510,6 @@ 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
@@ -602,13 +600,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: ObjectId = git.getRepository.resolve(s"refs/heads/${branch}") lazy val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${branch}")
lazy val mergeTip: ObjectId = git.getRepository.resolve(s"refs/pull/${issueId}/head") lazy val mergeTip = 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 {
@@ -617,7 +615,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 {
@@ -653,16 +651,14 @@ 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, commits: Seq[RevCommit])(implicit def merge(message: String, committer: PersonIdent, pusher: String)(implicit s: Session): ObjectId = {
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.")
} }
@@ -670,7 +666,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)
@@ -694,10 +690,10 @@ object MergeService {
hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true)
} }
MergeResult(objectId, commits.map(_.name())) objectId
} }
def rebase(committer: PersonIdent, pusher: String, commits: Seq[RevCommit])(implicit s: Session): MergeResult = { def rebase(committer: PersonIdent, pusher: String, commits: Seq[RevCommit])(implicit s: Session): ObjectId = {
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.")
} }
@@ -717,13 +713,11 @@ 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()
} }
@@ -751,10 +745,10 @@ object MergeService {
hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true)
} }
MergeResult(objectId, mergedCommitIds.result()) objectId
} }
def squash(message: String, committer: PersonIdent, pusher: String)(implicit s: Session): MergeResult = { def squash(message: String, committer: PersonIdent, pusher: String)(implicit s: Session): ObjectId = {
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.")
} }
@@ -810,7 +804,7 @@ object MergeService {
hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true) hook.postReceive(userName, repositoryName, receivePack, receiveCommand, committer.getName, true)
} }
MergeResult(objectId, Seq(newCommitId.name())) objectId
} }
// return treeId // return treeId
@@ -829,5 +823,4 @@ 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])
} }

View File

@@ -63,15 +63,6 @@ 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] =
@@ -135,8 +126,7 @@ trait PullRequestService {
requestBranch, requestBranch,
commitIdFrom, commitIdFrom,
commitIdTo, commitIdTo,
isDraft, isDraft
None
) )
// fetch requested branch // fetch requested branch
@@ -418,10 +408,11 @@ 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
} }

View File

@@ -265,7 +265,7 @@ trait WebHookService {
} }
private def validateTargetAddress(settings: SystemSettings, url: String): Boolean = { private def validateTargetAddress(settings: SystemSettings, url: String): Boolean = {
val host = new java.net.URL(url).getHost val host = new java.net.URI(url).toURL.getHost
!settings.webHook.blockPrivateAddress || !settings.webHook.blockPrivateAddress ||
!HttpClientUtil.isPrivateAddress(host) || !HttpClientUtil.isPrivateAddress(host) ||

View File

@@ -81,13 +81,4 @@
}); });
}); });
</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>
}
} }

View File

@@ -34,7 +34,7 @@
<div class="head" style="height: 24px;"> <div class="head" style="height: 24px;">
<div class="pull-right"> <div class="pull-right">
<div class="btn-group"> <div class="btn-group">
<a href="@{helpers.url(repository)}/archive@if(pathList.length > 0){/@pathList.map(helpers.urlEncode).mkString("/")}/@{helpers.urlEncode(branch)}.zip" class="btn btn-sm btn-default pc"><i class="octicon octicon-cloud-download"></i> Download ZIP</a> <a href="@{helpers.url(repository)}/archive/@{helpers.urlEncode(branch)}.zip@if(pathList.nonEmpty){?path=@helpers.urlEncode(pathList.mkString("/"))}" class="btn btn-sm btn-default pc"><i class="octicon octicon-cloud-download"></i> Download ZIP</a>
<a href="@helpers.url(repository)/find/@helpers.encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t" title="Search files"><i class="octicon octicon-search" aria-label="Search files"></i></a> <a href="@helpers.url(repository)/find/@helpers.encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t" title="Search files"><i class="octicon octicon-search" aria-label="Search files"></i></a>
<a href="@helpers.url(repository)/commits/@helpers.encodeRefName((branch :: pathList).mkString("/"))" class="btn btn-sm btn-default"><i class="octicon octicon-history"></i> @if(commitCount > 10000){10000+} else {@commitCount} @helpers.plural(commitCount, "commit")</a> <a href="@helpers.url(repository)/commits/@helpers.encodeRefName((branch :: pathList).mkString("/"))" class="btn btn-sm btn-default"><i class="octicon octicon-history"></i> @if(commitCount > 10000){10000+} else {@commitCount} @helpers.plural(commitCount, "commit")</a>
</div> </div>

View File

@@ -1,14 +1,14 @@
package gitbucket.core package gitbucket.core
import java.sql.DriverManager import java.sql.DriverManager
import com.dimafeng.testcontainers.{MySQLContainer, PostgreSQLContainer}
import io.github.gitbucket.solidbase.Solidbase import io.github.gitbucket.solidbase.Solidbase
import io.github.gitbucket.solidbase.model.Module import io.github.gitbucket.solidbase.model.Module
import liquibase.database.core.{H2Database, MySQLDatabase, PostgresDatabase} import liquibase.database.core.{H2Database, MySQLDatabase, PostgresDatabase}
import org.junit.runner.Description import org.junit.runner.Description
import org.scalatest.funsuite.AnyFunSuite import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.Tag import org.scalatest.Tag
import org.testcontainers.postgresql.PostgreSQLContainer
import org.testcontainers.mysql.MySQLContainer
import org.testcontainers.utility.DockerImageName import org.testcontainers.utility.DockerImageName
object ExternalDBTest extends Tag("ExternalDBTest") object ExternalDBTest extends Tag("ExternalDBTest")
@@ -26,24 +26,19 @@ class GitBucketCoreModuleSpec extends AnyFunSuite {
implicit private val suiteDescription: Description = Description.createSuiteDescription(getClass) implicit private val suiteDescription: Description = Description.createSuiteDescription(getClass)
Seq("8.0", "5.7").foreach { tag => Seq("8.4", "5.7").foreach { tag =>
test(s"Migration MySQL $tag", ExternalDBTest) { test(s"Migration MySQL $tag", ExternalDBTest) {
val container = new MySQLContainer() { val container = new MySQLContainer(s"mysql:$tag") {
override val container: org.testcontainers.containers.MySQLContainer[?] = override def getDriverClassName = "org.mariadb.jdbc.Driver"
new org.testcontainers.containers.MySQLContainer(s"mysql:$tag") { override def getJdbcUrl: String = super.getJdbcUrl + "?permitMysqlScheme"
override def getDriverClassName = "org.mariadb.jdbc.Driver"
override def getJdbcUrl: String = super.getJdbcUrl + "?permitMysqlScheme"
}
// TODO https://jira.mariadb.org/browse/CONJ-663
container.withCommand("mysqld --default-authentication-plugin=mysql_native_password")
} }
container.start() container.start()
try { try {
new Solidbase().migrate( new Solidbase().migrate(
DriverManager.getConnection( DriverManager.getConnection(
container.jdbcUrl, container.getJdbcUrl,
container.username, container.getUsername,
container.password container.getPassword
), ),
Thread.currentThread().getContextClassLoader(), Thread.currentThread().getContextClassLoader(),
new MySQLDatabase(), new MySQLDatabase(),
@@ -57,12 +52,12 @@ class GitBucketCoreModuleSpec extends AnyFunSuite {
Seq("11", "10").foreach { tag => Seq("11", "10").foreach { tag =>
test(s"Migration PostgreSQL $tag", ExternalDBTest) { test(s"Migration PostgreSQL $tag", ExternalDBTest) {
val container = PostgreSQLContainer(DockerImageName.parse(s"postgres:$tag")) val container = new PostgreSQLContainer(DockerImageName.parse(s"postgres:$tag"))
container.start() container.start()
try { try {
new Solidbase().migrate( new Solidbase().migrate(
DriverManager.getConnection(container.jdbcUrl, container.username, container.password), DriverManager.getConnection(container.getJdbcUrl, container.getUsername, container.getPassword),
Thread.currentThread().getContextClassLoader(), Thread.currentThread().getContextClassLoader(),
new PostgresDatabase(), new PostgresDatabase(),
new Module(GitBucketCoreModule.getModuleId, GitBucketCoreModule.getVersions) new Module(GitBucketCoreModule.getModuleId, GitBucketCoreModule.getVersions)

View File

@@ -183,14 +183,14 @@ class ApiIntegrationTest extends AnyFunSuite {
.branch("main") .branch("main")
.content("create") .content("create")
.message("Create content") .message("Create content")
.path("README.md") .path("test.txt")
.commit() .commit()
assert(createResult.getContent.isFile == true) assert(createResult.getContent.isFile)
assert(IOUtils.toString(createResult.getContent.read(), "UTF-8") == "create") assert(IOUtils.toString(createResult.getContent.read(), "UTF-8") == "create")
val content1 = repo.getFileContent("README.md") val content1 = repo.getFileContent("test.txt")
assert(content1.isFile == true) assert(content1.isFile)
assert(IOUtils.toString(content1.read(), "UTF-8") == "create") assert(IOUtils.toString(content1.read(), "UTF-8") == "create")
assert(content1.getSha == createResult.getContent.getSha) assert(content1.getSha == createResult.getContent.getSha)
@@ -200,14 +200,14 @@ class ApiIntegrationTest extends AnyFunSuite {
.branch("main") .branch("main")
.content("update") .content("update")
.message("Update content") .message("Update content")
.path("README.md") .path("test.txt")
.sha(content1.getSha) .sha(content1.getSha)
.commit() .commit()
assert(updateResult.getContent.isFile == true) assert(updateResult.getContent.isFile)
assert(IOUtils.toString(updateResult.getContent.read(), "UTF-8") == "update") assert(IOUtils.toString(updateResult.getContent.read(), "UTF-8") == "update")
val content2 = repo.getFileContent("README.md") val content2 = repo.getFileContent("test.txt")
assert(content2.isFile == true) assert(content2.isFile == true)
assert(IOUtils.toString(content2.read(), "UTF-8") == "update") assert(IOUtils.toString(content2.read(), "UTF-8") == "update")
assert(content2.getSha == updateResult.getContent.getSha) assert(content2.getSha == updateResult.getContent.getSha)

View File

@@ -154,8 +154,7 @@ 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(

View File

@@ -171,7 +171,6 @@ 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