diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 04eb0236345..0dd9a6687d8 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -16,10 +16,10 @@ parserOptions: parser: "@typescript-eslint/parser" # for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser settings: - import/extensions: [".js", ".ts"] - import/parsers: + import-x/extensions: [".js", ".ts"] + import-x/parsers: "@typescript-eslint/parser": [".js", ".ts"] - import/resolver: + import-x/resolver: typescript: true plugins: @@ -28,7 +28,7 @@ plugins: - "@typescript-eslint/eslint-plugin" - eslint-plugin-array-func - eslint-plugin-github - - eslint-plugin-i + - eslint-plugin-import-x - eslint-plugin-no-jquery - eslint-plugin-no-use-extend-native - eslint-plugin-regexp @@ -58,15 +58,15 @@ overrides: no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top] - files: ["*.config.*"] rules: - i/no-unused-modules: [0] + import-x/no-unused-modules: [0] - files: ["**/*.d.ts"] rules: - i/no-unused-modules: [0] + import-x/no-unused-modules: [0] "@typescript-eslint/consistent-type-definitions": [0] "@typescript-eslint/consistent-type-imports": [0] - files: ["web_src/js/types.ts"] rules: - i/no-unused-modules: [0] + import-x/no-unused-modules: [0] - files: ["**/*.test.*", "web_src/js/test/setup.ts"] env: vitest-globals/env: true @@ -394,49 +394,49 @@ rules: id-blacklist: [0] id-length: [0] id-match: [0] - i/consistent-type-specifier-style: [0] - i/default: [0] - i/dynamic-import-chunkname: [0] - i/export: [2] - i/exports-last: [0] - i/extensions: [2, always, {ignorePackages: true}] - i/first: [2] - i/group-exports: [0] - i/max-dependencies: [0] - i/named: [2] - i/namespace: [0] - i/newline-after-import: [0] - i/no-absolute-path: [0] - i/no-amd: [2] - i/no-anonymous-default-export: [0] - i/no-commonjs: [2] - i/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}] - i/no-default-export: [0] - i/no-deprecated: [0] - i/no-dynamic-require: [0] - i/no-empty-named-blocks: [2] - i/no-extraneous-dependencies: [2] - i/no-import-module-exports: [0] - i/no-internal-modules: [0] - i/no-mutable-exports: [0] - i/no-named-as-default-member: [0] - i/no-named-as-default: [0] - i/no-named-default: [0] - i/no-named-export: [0] - i/no-namespace: [0] - i/no-nodejs-modules: [0] - i/no-relative-packages: [0] - i/no-relative-parent-imports: [0] - i/no-restricted-paths: [0] - i/no-self-import: [2] - i/no-unassigned-import: [0] - i/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}] - i/no-unused-modules: [2, {unusedExports: true}] - i/no-useless-path-segments: [2, {commonjs: true}] - i/no-webpack-loader-syntax: [2] - i/order: [0] - i/prefer-default-export: [0] - i/unambiguous: [0] + import-x/consistent-type-specifier-style: [0] + import-x/default: [0] + import-x/dynamic-import-chunkname: [0] + import-x/export: [2] + import-x/exports-last: [0] + import-x/extensions: [2, always, {ignorePackages: true}] + import-x/first: [2] + import-x/group-exports: [0] + import-x/max-dependencies: [0] + import-x/named: [2] + import-x/namespace: [0] + import-x/newline-after-import: [0] + import-x/no-absolute-path: [0] + import-x/no-amd: [2] + import-x/no-anonymous-default-export: [0] + import-x/no-commonjs: [2] + import-x/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}] + import-x/no-default-export: [0] + import-x/no-deprecated: [0] + import-x/no-dynamic-require: [0] + import-x/no-empty-named-blocks: [2] + import-x/no-extraneous-dependencies: [2] + import-x/no-import-module-exports: [0] + import-x/no-internal-modules: [0] + import-x/no-mutable-exports: [0] + import-x/no-named-as-default-member: [0] + import-x/no-named-as-default: [0] + import-x/no-named-default: [0] + import-x/no-named-export: [0] + import-x/no-namespace: [0] + import-x/no-nodejs-modules: [0] + import-x/no-relative-packages: [0] + import-x/no-relative-parent-imports: [0] + import-x/no-restricted-paths: [0] + import-x/no-self-import: [2] + import-x/no-unassigned-import: [0] + import-x/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}] + import-x/no-unused-modules: [2, {unusedExports: true}] + import-x/no-useless-path-segments: [2, {commonjs: true}] + import-x/no-webpack-loader-syntax: [2] + import-x/order: [0] + import-x/prefer-default-export: [0] + import-x/unambiguous: [0] init-declarations: [0] line-comment-position: [0] logical-assignment-operators: [0] diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 023fb05a296..2adea23aa40 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -3,3 +3,5 @@ self-hosted-runner: - actuated-4cpu-8gb - actuated-4cpu-16gb - nscloud + - namespace-profile-gitea-release-docker + - namespace-profile-gitea-release-binary diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 6e1b6e07584..2264c9e822d 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -10,7 +10,7 @@ concurrency: jobs: nightly-binary: - runs-on: nscloud + runs-on: namespace-profile-gitea-release-binary steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions @@ -58,7 +58,7 @@ jobs: run: | aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress nightly-docker-rootful: - runs-on: ubuntu-latest + runs-on: namespace-profile-gitea-release-docker steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions @@ -95,7 +95,7 @@ jobs: push: true tags: gitea/gitea:${{ steps.clean_name.outputs.branch }} nightly-docker-rootless: - runs-on: ubuntu-latest + runs-on: namespace-profile-gitea-release-docker steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index 41037df29cb..a406602dc0a 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -11,7 +11,7 @@ concurrency: jobs: binary: - runs-on: nscloud + runs-on: namespace-profile-gitea-release-binary steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions @@ -68,7 +68,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} docker-rootful: - runs-on: ubuntu-latest + runs-on: namespace-profile-gitea-release-docker steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions @@ -99,7 +99,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} docker-rootless: - runs-on: ubuntu-latest + runs-on: namespace-profile-gitea-release-docker steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index a23e6982000..f67b76f4087 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -13,7 +13,7 @@ concurrency: jobs: binary: - runs-on: nscloud + runs-on: namespace-profile-gitea-release-binary steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions @@ -70,7 +70,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} docker-rootful: - runs-on: ubuntu-latest + runs-on: namespace-profile-gitea-release-docker steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions @@ -105,7 +105,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} docker-rootless: - runs-on: ubuntu-latest + runs-on: namespace-profile-gitea-release-docker steps: - uses: actions/checkout@v4 # fetch all commits instead of only the last as some branches are long lived and could have many between versions diff --git a/go.mod b/go.mod index 80b62ce83f8..671151d4b63 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/ethantkoenig/rupture v1.0.1 github.com/felixge/fgprof v0.9.5 github.com/fsnotify/fsnotify v1.7.0 - github.com/gliderlabs/ssh v0.3.7 + github.com/gliderlabs/ssh v0.3.8 github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-chi/chi/v5 v5.1.0 @@ -121,13 +121,13 @@ require ( github.com/yuin/goldmark v1.7.8 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-meta v1.1.0 - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.31.0 golang.org/x/image v0.21.0 golang.org/x/net v0.30.0 golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.8.0 - golang.org/x/sys v0.26.0 - golang.org/x/text v0.19.0 + golang.org/x/sync v0.10.0 + golang.org/x/sys v0.28.0 + golang.org/x/text v0.21.0 golang.org/x/tools v0.26.0 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 diff --git a/go.sum b/go.sum index d1b7890fb68..afa3abece8f 100644 --- a/go.sum +++ b/go.sum @@ -293,8 +293,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0= github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A= -github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= -github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c h1:82lzmsy5Nr6JA6HcLRVxGfbdSoWfW45C6jnY3zFS7Ks= @@ -893,8 +893,9 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= @@ -946,8 +947,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -982,8 +984,9 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -996,8 +999,9 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1009,8 +1013,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/models/actions/run.go b/models/actions/run.go index 732fb48bb9a..f40bc1eb3db 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -37,6 +37,7 @@ type ActionRun struct { TriggerUser *user_model.User `xorm:"-"` ScheduleID int64 Ref string `xorm:"index"` // the commit/tag/… that caused the run + IsRefDeleted bool `xorm:"-"` CommitSHA string IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow. NeedApproval bool // may need approval if it's a fork pull request diff --git a/models/db/context_committer_test.go b/models/db/context_committer_test.go index 38e91f22edb..849c5dea411 100644 --- a/models/db/context_committer_test.go +++ b/models/db/context_committer_test.go @@ -4,7 +4,7 @@ package db // it's not db_test, because this file is for testing the private type halfCommitter import ( - "fmt" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -80,7 +80,7 @@ func Test_halfCommitter(t *testing.T) { testWithCommitter(mockCommitter, func(committer Committer) error { defer committer.Close() if true { - return fmt.Errorf("error") + return errors.New("error") } return committer.Commit() }) @@ -94,7 +94,7 @@ func Test_halfCommitter(t *testing.T) { testWithCommitter(mockCommitter, func(committer Committer) error { committer.Close() committer.Commit() - return fmt.Errorf("error") + return errors.New("error") }) mockCommitter.Assert(t) diff --git a/models/git/branch.go b/models/git/branch.go index ba1ada5517d..e683ce47e65 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -12,6 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -169,9 +170,22 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e return &branch, nil } -func GetBranches(ctx context.Context, repoID int64, branchNames []string) ([]*Branch, error) { +func GetBranches(ctx context.Context, repoID int64, branchNames []string, includeDeleted bool) ([]*Branch, error) { branches := make([]*Branch, 0, len(branchNames)) - return branches, db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames).Find(&branches) + + sess := db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames) + if !includeDeleted { + sess.And("is_deleted=?", false) + } + return branches, sess.Find(&branches) +} + +func BranchesToNamesSet(branches []*Branch) container.Set[string] { + names := make(container.Set[string], len(branches)) + for _, branch := range branches { + names.Add(branch.Name) + } + return names } func AddBranches(ctx context.Context, branches []*Branch) error { diff --git a/models/issues/issue.go b/models/issues/issue.go index 64fc20cc05e..fe347c27156 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -125,8 +125,11 @@ type Issue struct { IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not. PullRequest *PullRequest `xorm:"-"` NumComments int - Ref string - PinOrder int `xorm:"DEFAULT 0"` + + // TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl" + Ref string + + PinOrder int `xorm:"DEFAULT 0"` DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"` diff --git a/modules/git/commit.go b/modules/git/commit.go index 010b56948ef..0ed268e3469 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -474,3 +474,17 @@ func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSetting } return c.repo.GetDefaultPublicGPGKey(forceUpdate) } + +func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool { + minLen := util.OptionalArg(minLength, objFmt.FullLength()) + if len(s) < minLen || len(s) > objFmt.FullLength() { + return false + } + for _, c := range s { + isHex := (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') + if !isHex { + return false + } + } + return true +} diff --git a/modules/git/ref.go b/modules/git/ref.go index 2db630e2ea9..aab4c5d77d7 100644 --- a/modules/git/ref.go +++ b/modules/git/ref.go @@ -142,7 +142,6 @@ func (ref RefName) RemoteName() string { // ShortName returns the short name of the reference name func (ref RefName) ShortName() string { - refName := string(ref) if ref.IsBranch() { return ref.BranchName() } @@ -158,8 +157,7 @@ func (ref RefName) ShortName() string { if ref.IsFor() { return ref.ForBranchName() } - - return refName + return string(ref) // usually it is a commit ID } // RefGroup returns the group type of the reference diff --git a/modules/git/repo_ref.go b/modules/git/repo_ref.go index 8eaa17cb041..850ec655029 100644 --- a/modules/git/repo_ref.go +++ b/modules/git/repo_ref.go @@ -61,3 +61,31 @@ func parseTags(refs []string) []string { } return results } + +// UnstableGuessRefByShortName does the best guess to see whether a "short name" provided by user is a branch, tag or commit. +// It could guess wrongly if the input is already ambiguous. For example: +// * "refs/heads/the-name" vs "refs/heads/refs/heads/the-name" +// * "refs/tags/1234567890" vs commit "1234567890" +// In most cases, it SHOULD AVOID using this function, unless there is an irresistible reason (eg: make API friendly to end users) +// If the function is used, the caller SHOULD CHECK the ref type carefully. +func (repo *Repository) UnstableGuessRefByShortName(shortName string) RefName { + if repo.IsBranchExist(shortName) { + return RefNameFromBranch(shortName) + } + if repo.IsTagExist(shortName) { + return RefNameFromTag(shortName) + } + if strings.HasPrefix(shortName, "refs/") { + if repo.IsReferenceExist(shortName) { + return RefName(shortName) + } + } + commit, err := repo.GetCommit(shortName) + if err == nil { + commitIDString := commit.ID.String() + if strings.HasPrefix(commitIDString, shortName) { + return RefName(commitIDString) + } + } + return "" +} diff --git a/modules/globallock/globallock_test.go b/modules/globallock/globallock_test.go index 88a555c86f3..f14c7d656b6 100644 --- a/modules/globallock/globallock_test.go +++ b/modules/globallock/globallock_test.go @@ -64,7 +64,7 @@ func TestLockAndDo(t *testing.T) { } func testLockAndDo(t *testing.T) { - const concurrency = 1000 + const concurrency = 50 ctx := context.Background() count := 0 diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 7ef370e89c5..bf51bd6c148 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -23,7 +23,7 @@ import ( const ( issueIndexerAnalyzer = "issueIndexer" issueIndexerDocType = "issueIndexerDocType" - issueIndexerLatestVersion = 4 + issueIndexerLatestVersion = 5 ) const unicodeNormalizeName = "unicodeNormalize" @@ -75,6 +75,7 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) { docMapping.AddFieldMappingsAt("is_pull", boolFieldMapping) docMapping.AddFieldMappingsAt("is_closed", boolFieldMapping) + docMapping.AddFieldMappingsAt("is_archived", boolFieldMapping) docMapping.AddFieldMappingsAt("label_ids", numberFieldMapping) docMapping.AddFieldMappingsAt("no_label", boolFieldMapping) docMapping.AddFieldMappingsAt("milestone_id", numberFieldMapping) @@ -185,6 +186,9 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.IsClosed.Has() { queries = append(queries, inner_bleve.BoolFieldQuery(options.IsClosed.Value(), "is_closed")) } + if options.IsArchived.Has() { + queries = append(queries, inner_bleve.BoolFieldQuery(options.IsArchived.Value(), "is_archived")) + } if options.NoLabelOnly { queries = append(queries, inner_bleve.BoolFieldQuery(true, "no_label")) diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 98b097f8713..42834f6e886 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -72,7 +72,7 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m UpdatedAfterUnix: options.UpdatedAfterUnix.Value(), UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(), PriorityRepoID: 0, - IsArchived: optional.None[bool](), + IsArchived: options.IsArchived, Org: nil, Team: nil, User: nil, diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 1a0f241e615..4f6ad96d222 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -11,11 +11,12 @@ import ( func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions { searchOpt := &SearchOptions{ - Keyword: keyword, - RepoIDs: opts.RepoIDs, - AllPublic: opts.AllPublic, - IsPull: opts.IsPull, - IsClosed: opts.IsClosed, + Keyword: keyword, + RepoIDs: opts.RepoIDs, + AllPublic: opts.AllPublic, + IsPull: opts.IsPull, + IsClosed: opts.IsClosed, + IsArchived: opts.IsArchived, } if len(opts.LabelIDs) == 1 && opts.LabelIDs[0] == 0 { diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 6f705150097..4c293f3f2a9 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -18,7 +18,7 @@ import ( ) const ( - issueIndexerLatestVersion = 1 + issueIndexerLatestVersion = 2 // multi-match-types, currently only 2 types are used // Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types esMultiMatchTypeBestFields = "best_fields" @@ -58,6 +58,7 @@ const ( "is_pull": { "type": "boolean", "index": true }, "is_closed": { "type": "boolean", "index": true }, + "is_archived": { "type": "boolean", "index": true }, "label_ids": { "type": "integer", "index": true }, "no_label": { "type": "boolean", "index": true }, "milestone_id": { "type": "integer", "index": true }, @@ -168,6 +169,9 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.IsClosed.Has() { query.Must(elastic.NewTermQuery("is_closed", options.IsClosed.Value())) } + if options.IsArchived.Has() { + query.Must(elastic.NewTermQuery("is_archived", options.IsArchived.Value())) + } if options.NoLabelOnly { query.Must(elastic.NewTermQuery("no_label", true)) diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 7c3ba75bb07..06a6a46c234 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -37,6 +37,7 @@ func TestDBSearchIssues(t *testing.T) { t.Run("search issues by ID", searchIssueByID) t.Run("search issues is pr", searchIssueIsPull) t.Run("search issues is closed", searchIssueIsClosed) + t.Run("search issues is archived", searchIssueIsArchived) t.Run("search issues by milestone", searchIssueByMilestoneID) t.Run("search issues by label", searchIssueByLabelID) t.Run("search issues by time", searchIssueByTime) @@ -298,6 +299,33 @@ func searchIssueIsClosed(t *testing.T) { } } +func searchIssueIsArchived(t *testing.T) { + tests := []struct { + opts SearchOptions + expectedIDs []int64 + }{ + { + SearchOptions{ + IsArchived: optional.Some(false), + }, + []int64{22, 21, 17, 16, 15, 13, 12, 11, 20, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2, 1}, + }, + { + SearchOptions{ + IsArchived: optional.Some(true), + }, + []int64{14}, + }, + } + for _, test := range tests { + issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + if !assert.NoError(t, err) { + return + } + assert.Equal(t, test.expectedIDs, issueIDs) + } +} + func searchIssueByMilestoneID(t *testing.T) { tests := []struct { opts SearchOptions diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index a43c6be0059..09dcbf4804c 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -25,6 +25,7 @@ type IndexerData struct { // Fields used for filtering IsPull bool `json:"is_pull"` IsClosed bool `json:"is_closed"` + IsArchived bool `json:"is_archived"` LabelIDs []int64 `json:"label_ids"` NoLabel bool `json:"no_label"` // True if LabelIDs is empty MilestoneID int64 `json:"milestone_id"` @@ -81,8 +82,9 @@ type SearchOptions struct { RepoIDs []int64 // repository IDs which the issues belong to AllPublic bool // if include all public repositories - IsPull optional.Option[bool] // if the issues is a pull request - IsClosed optional.Option[bool] // if the issues is closed + IsPull optional.Option[bool] // if the issues is a pull request + IsClosed optional.Option[bool] // if the issues is closed + IsArchived optional.Option[bool] // if the repo is archived IncludedLabelIDs []int64 // labels the issues have ExcludedLabelIDs []int64 // labels the issues don't have diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 93323193392..1066e962725 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -18,7 +18,7 @@ import ( ) const ( - issueIndexerLatestVersion = 3 + issueIndexerLatestVersion = 4 // TODO: make this configurable if necessary maxTotalHits = 10000 @@ -61,6 +61,7 @@ func NewIndexer(url, apiKey, indexerName string) *Indexer { "is_public", "is_pull", "is_closed", + "is_archived", "label_ids", "no_label", "milestone_id", @@ -145,6 +146,9 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.IsClosed.Has() { query.And(inner_meilisearch.NewFilterEq("is_closed", options.IsClosed.Value())) } + if options.IsArchived.Has() { + query.And(inner_meilisearch.NewFilterEq("is_archived", options.IsArchived.Value())) + } if options.NoLabelOnly { query.And(inner_meilisearch.NewFilterEq("no_label", true)) diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go index e752ae6f243..deb19adc49d 100644 --- a/modules/indexer/issues/util.go +++ b/modules/indexer/issues/util.go @@ -101,6 +101,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD Comments: comments, IsPull: issue.IsPull, IsClosed: issue.IsClosed, + IsArchived: issue.Repo.IsArchived, LabelIDs: labels, NoLabel: len(labels) == 0, MilestoneID: issue.MilestoneID, diff --git a/modules/lfs/http_client.go b/modules/lfs/http_client.go index 3060e25754c..50f0e7a8d88 100644 --- a/modules/lfs/http_client.go +++ b/modules/lfs/http_client.go @@ -236,6 +236,7 @@ func createRequest(ctx context.Context, method, url string, headers map[string]s req.Header.Set(key, value) } req.Header.Set("Accept", AcceptHeader) + req.Header.Set("User-Agent", UserAgentHeader) return req, nil } diff --git a/modules/lfs/shared.go b/modules/lfs/shared.go index a4326b57b2f..40ad789c1d9 100644 --- a/modules/lfs/shared.go +++ b/modules/lfs/shared.go @@ -15,7 +15,8 @@ const ( // MediaType contains the media type for LFS server requests MediaType = "application/vnd.git-lfs+json" // Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served - AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8" + AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8" + UserAgentHeader = "git-lfs" ) // BatchRequest contains multiple requests processed in one batch operation. diff --git a/modules/markup/markdown/markdown_math_test.go b/modules/markup/markdown/markdown_math_test.go index e371b1c74ab..a2213b2ce76 100644 --- a/modules/markup/markdown/markdown_math_test.go +++ b/modules/markup/markdown/markdown_math_test.go @@ -20,23 +20,23 @@ func TestMathRender(t *testing.T) { }{ { "$a$", - `
a
a
a
a
a
b
a
b
a
b
a
b
a
.
a
.
a
(b
) [$c$] {$d$}
a
(b
) [$c$] {$d$}
a
` + nl,
+ `a
` + nl,
},
{
"$$a$$ test",
- `a
test
a
test
test a
test a
foo x=\$
bar
foo x=\$
bar
\text{$b$}
\text{$b$}
+ `
\alpha
`,
@@ -122,7 +122,7 @@ func TestMathRenderBlockIndent(t *testing.T) {
\alpha
\]
`,
- `
+ `
\alpha
`,
@@ -137,7 +137,7 @@ a
d
\]
`,
- `
+ `
a
b
c
@@ -154,7 +154,7 @@ c
c
\]
`,
- `
+ `
a
b
c
@@ -165,7 +165,7 @@ c
"indent-0-oneline",
`$$ x $$
foo`,
- ` x
+ ` x
foo
`,
},
@@ -173,7 +173,7 @@ foo`,
"indent-3-oneline",
` $$ x $$
foo`,
- ` x
+ ` x
foo
`,
},
@@ -188,10 +188,10 @@ foo`,
> \]
`,
`
-
+
a
-
+
b
@@ -207,7 +207,7 @@ b
2. b`,
`
- a
-
+
x
diff --git a/modules/markup/markdown/math/block_renderer.go b/modules/markup/markdown/math/block_renderer.go
index a770efa01c7..c29f0618821 100644
--- a/modules/markup/markdown/math/block_renderer.go
+++ b/modules/markup/markdown/math/block_renderer.go
@@ -12,6 +12,17 @@ import (
"github.com/yuin/goldmark/util"
)
+// Block render output:
+// ...
+//
+// Keep in mind that there is another "code block" render in "func (r *GlodmarkRender) highlightingRenderer"
+// "highlightingRenderer" outputs the math block with extra "chroma" class:
+// ...
+//
+// Special classes:
+// * "is-loading": show a loading indicator
+// * "display": used by JS to decide to render as a block, otherwise render as inline
+
// BlockRenderer represents a renderer for math Blocks
type BlockRenderer struct {
renderInternal *internal.RenderInternal
@@ -38,7 +49,7 @@ func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node)
func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
n := node.(*Block)
if entering {
- code := giteaUtil.Iif(n.Inline, "", ``) + ``
+ code := giteaUtil.Iif(n.Inline, "", ``) + ``
_ = r.renderInternal.FormatWithSafeAttrs(w, code)
r.writeLines(w, source, n)
} else {
diff --git a/modules/markup/markdown/math/inline_renderer.go b/modules/markup/markdown/math/inline_renderer.go
index 0cff4f1e74e..4e0531cf404 100644
--- a/modules/markup/markdown/math/inline_renderer.go
+++ b/modules/markup/markdown/math/inline_renderer.go
@@ -13,6 +13,9 @@ import (
"github.com/yuin/goldmark/util"
)
+// Inline render output:
+// ...
+
// InlineRenderer is an inline renderer
type InlineRenderer struct {
renderInternal *internal.RenderInternal
@@ -25,11 +28,7 @@ func NewInlineRenderer(renderInternal *internal.RenderInternal) renderer.NodeRen
func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- extraClass := ""
- if _, ok := n.(*InlineBlock); ok {
- extraClass = "display "
- }
- _ = r.renderInternal.FormatWithSafeAttrs(w, ``, extraClass)
+ _ = r.renderInternal.FormatWithSafeAttrs(w, ``)
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
segment := c.(*ast.Text).Segment
value := util.EscapeHTML(segment.Value(source))
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index f8e4f569b87..6d0695ee163 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -13,10 +13,12 @@ import (
"errors"
"fmt"
"io"
+ "maps"
"net"
"os"
"os/exec"
"path/filepath"
+ "reflect"
"strconv"
"strings"
"sync"
@@ -33,9 +35,22 @@ import (
gossh "golang.org/x/crypto/ssh"
)
-type contextKey string
+// The ssh auth overall works like this:
+// NewServerConn:
+// serverHandshake+serverAuthenticate:
+// PublicKeyCallback:
+// PublicKeyHandler (our code):
+// reset(ctx.Permissions) and set ctx.Permissions.giteaKeyID = keyID
+// pubKey.Verify
+// return ctx.Permissions // only reaches here, the pub key is really authenticated
+// set conn.Permissions from serverAuthenticate
+// sessionHandler(conn)
+//
+// Then sessionHandler should only use the "verified keyID" from the original ssh conn, but not the ctx one.
+// Otherwise, if a user provides 2 keys A (a correct one) and B (public key matches but no private key),
+// then only A succeeds to authenticate, sessionHandler will see B's keyID
-const giteaKeyID = contextKey("gitea-key-id")
+const giteaPermissionExtensionKeyID = "gitea-perm-ext-key-id"
func getExitStatusFromError(err error) int {
if err == nil {
@@ -61,8 +76,32 @@ func getExitStatusFromError(err error) int {
return waitStatus.ExitStatus()
}
+// sessionPartial is the private struct from "gliderlabs/ssh/session.go"
+// We need to read the original "conn" field from "ssh.Session interface" which contains the "*session pointer"
+// https://github.com/gliderlabs/ssh/blob/d137aad99cd6f2d9495bfd98c755bec4e5dffb8c/session.go#L109-L113
+// If upstream fixes the problem and/or changes the struct, we need to follow.
+// If the struct mismatches, the builtin ssh server will fail during integration tests.
+type sessionPartial struct {
+ sync.Mutex
+ gossh.Channel
+ conn *gossh.ServerConn
+}
+
+func ptr[T any](intf any) *T {
+ // https://pkg.go.dev/unsafe#Pointer
+ // (1) Conversion of a *T1 to Pointer to *T2.
+ // Provided that T2 is no larger than T1 and that the two share an equivalent memory layout,
+ // this conversion allows reinterpreting data of one type as data of another type.
+ v := reflect.ValueOf(intf)
+ p := v.UnsafePointer()
+ return (*T)(p)
+}
+
func sessionHandler(session ssh.Session) {
- keyID := fmt.Sprintf("%d", session.Context().Value(giteaKeyID).(int64))
+ // here can't use session.Permissions() because it only uses the value from ctx, which might not be the authenticated one.
+ // so we must use the original ssh conn, which always contains the correct (verified) keyID.
+ sshConn := ptr[sessionPartial](session)
+ keyID := sshConn.conn.Permissions.Extensions[giteaPermissionExtensionKeyID]
command := session.RawCommand()
@@ -164,6 +203,23 @@ func sessionHandler(session ssh.Session) {
}
func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
+ // The publicKeyHandler (PublicKeyCallback) only helps to provide the candidate keys to authenticate,
+ // It does NOT really verify here, so we could only record the related information here.
+ // After authentication (Verify), the "Permissions" will be assigned to the ssh conn,
+ // then we can use it in the "session handler"
+
+ // first, reset the ctx permissions (just like https://github.com/gliderlabs/ssh/pull/243 does)
+ // it shouldn't be reused across different ssh conn (sessions), each pub key should have its own "Permissions"
+ oldCtxPerm := ctx.Permissions().Permissions
+ ctx.Permissions().Permissions = &gossh.Permissions{}
+ ctx.Permissions().Permissions.CriticalOptions = maps.Clone(oldCtxPerm.CriticalOptions)
+
+ setPermExt := func(keyID int64) {
+ ctx.Permissions().Permissions.Extensions = map[string]string{
+ giteaPermissionExtensionKeyID: fmt.Sprint(keyID),
+ }
+ }
+
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
log.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr())
}
@@ -238,8 +294,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
log.Debug("Successfully authenticated: %s Certificate Fingerprint: %s Principal: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key), principal)
}
- ctx.SetValue(giteaKeyID, pkey.ID)
-
+ setPermExt(pkey.ID)
return true
}
@@ -266,8 +321,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
log.Debug("Successfully authenticated: %s Public Key Fingerprint: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key))
}
- ctx.SetValue(giteaKeyID, pkey.ID)
-
+ setPermExt(pkey.ID)
return true
}
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 832ffa8bcc9..fb784bd8b37 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -278,6 +278,16 @@ type CreateBranchRepoOption struct {
OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"`
}
+// UpdateBranchRepoOption options when updating a branch in a repository
+// swagger:model
+type UpdateBranchRepoOption struct {
+ // New branch name
+ //
+ // required: true
+ // unique: true
+ Name string `json:"name" binding:"Required;GitRefName;MaxSize(100)"`
+}
+
// TransferRepoOption options when transfer a repository's ownership
// swagger:model
type TransferRepoOption struct {
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index f0abce0d4bf..776d2bdc2bf 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -47,7 +47,7 @@ webauthn_error_unknown=Ocorreu um erro desconhecido. Tente novamente, por favor.
webauthn_error_insecure=`WebAuthn apenas suporta conexões seguras. Para testar sobre HTTP, pode usar a origem "localhost" ou "127.0.0.1"`
webauthn_error_unable_to_process=O servidor não conseguiu processar o seu pedido.
webauthn_error_duplicated=A chave de segurança não é permitida neste pedido. Certifique-se de que a chave não está já registada.
-webauthn_error_empty=Você tem que definir um nome para esta chave.
+webauthn_error_empty=Tem de definir um nome para esta chave.
webauthn_error_timeout=O tempo limite foi atingido antes que a sua chave pudesse ser lida. Recarregue esta página e tente novamente.
webauthn_reload=Recarregar
@@ -1109,6 +1109,7 @@ delete_preexisting_success=Eliminados os ficheiros não adoptados em %s
blame_prior=Ver a responsabilização anterior a esta modificação
blame.ignore_revs=Ignorando as revisões em .git-blame-ignore-revs. Clique aqui para contornar e ver a vista normal de responsabilização.
blame.ignore_revs.failed=Falhou ao ignorar as revisões em .git-blame-ignore-revs.
+user_search_tooltip=Mostra um máximo de 30 utilizadores
tree_path_not_found_commit=A localização %[1]s não existe no cometimento %[2]s
tree_path_not_found_branch=A localização %[1]s não existe no ramo %[2]s
@@ -1527,6 +1528,8 @@ issues.filter_assignee=Encarregado
issues.filter_assginee_no_select=Todos os encarregados
issues.filter_assginee_no_assignee=Sem encarregado
issues.filter_poster=Autor(a)
+issues.filter_user_placeholder=Procurar utilizadores
+issues.filter_user_no_select=Todos os utilizadores
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas as questões
issues.filter_type.assigned_to_you=Atribuídas a si
diff --git a/package-lock.json b/package-lock.json
index e3f7a0116f7..53bd5bc4f1f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -87,7 +87,7 @@
"eslint-import-resolver-typescript": "3.7.0",
"eslint-plugin-array-func": "4.0.0",
"eslint-plugin-github": "5.1.3",
- "eslint-plugin-i": "2.29.1",
+ "eslint-plugin-import-x": "4.5.0",
"eslint-plugin-no-jquery": "3.1.0",
"eslint-plugin-no-use-extend-native": "0.5.0",
"eslint-plugin-playwright": "2.1.0",
@@ -8385,56 +8385,6 @@
"node": "*"
}
},
- "node_modules/eslint-plugin-i": {
- "version": "2.29.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-i/-/eslint-plugin-i-2.29.1.tgz",
- "integrity": "sha512-ORizX37MelIWLbMyqI7hi8VJMf7A0CskMmYkB+lkCX3aF4pkGV7kwx5bSEb4qx7Yce2rAf9s34HqDRPjGRZPNQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.4",
- "doctrine": "^3.0.0",
- "eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.8.0",
- "get-tsconfig": "^4.7.2",
- "is-glob": "^4.0.3",
- "minimatch": "^3.1.2",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://opencollective.com/unts"
- },
- "peerDependencies": {
- "eslint": "^7.2.0 || ^8"
- }
- },
- "node_modules/eslint-plugin-i/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint-plugin-i/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/eslint-plugin-i18n-text": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz",
@@ -8479,6 +8429,48 @@
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
}
},
+ "node_modules/eslint-plugin-import-x": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.5.0.tgz",
+ "integrity": "sha512-l0OTfnPF8RwmSXfjT75N8d6ZYLVrVYWpaGlgvVkVqFERCI5SyBfDP7QEMr3kt0zWi2sOa9EQ47clbdFsHkF83Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "^8.1.0",
+ "@typescript-eslint/utils": "^8.1.0",
+ "debug": "^4.3.4",
+ "doctrine": "^3.0.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "get-tsconfig": "^4.7.3",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.3",
+ "semver": "^7.6.3",
+ "stable-hash": "^0.0.4",
+ "tslib": "^2.6.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-import-x/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/eslint-plugin-import/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
diff --git a/package.json b/package.json
index d30aedc54f5..3a81e648228 100644
--- a/package.json
+++ b/package.json
@@ -86,7 +86,7 @@
"eslint-import-resolver-typescript": "3.7.0",
"eslint-plugin-array-func": "4.0.0",
"eslint-plugin-github": "5.1.3",
- "eslint-plugin-i": "2.29.1",
+ "eslint-plugin-import-x": "4.5.0",
"eslint-plugin-no-jquery": "3.1.0",
"eslint-plugin-no-use-extend-native": "0.5.0",
"eslint-plugin-playwright": "2.1.0",
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index 4e194f65fa1..47ea7137b82 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -358,6 +358,7 @@ func CommonRoutes() *web.Router {
r.Get("/PACKAGES", cran.EnumerateSourcePackages)
r.Get("/PACKAGES{format}", cran.EnumerateSourcePackages)
r.Get("/{filename}", cran.DownloadSourcePackageFile)
+ r.Get("/Archive/{packagename}/{filename}", cran.DownloadSourcePackageFile)
})
r.Put("", reqPackageAccess(perm.AccessModeWrite), cran.UploadSourcePackageFile)
})
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index f28ee980e10..96365e7c14d 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1195,6 +1195,7 @@ func Routes() *web.Router {
m.Get("/*", repo.GetBranch)
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch)
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch)
+ m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.UpdateBranchRepoOption{}), repo.UpdateBranch)
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode))
m.Group("/branch_protections", func() {
m.Get("", repo.ListBranchProtections)
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index 53f3b4648a5..946203e97ec 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -386,6 +386,77 @@ func ListBranches(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, apiBranches)
}
+// UpdateBranch updates a repository's branch.
+func UpdateBranch(ctx *context.APIContext) {
+ // swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoUpdateBranch
+ // ---
+ // summary: Update a branch
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: branch
+ // in: path
+ // description: name of the branch
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/UpdateBranchRepoOption"
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+
+ opt := web.GetForm(ctx).(*api.UpdateBranchRepoOption)
+
+ oldName := ctx.PathParam("*")
+ repo := ctx.Repo.Repository
+
+ if repo.IsEmpty {
+ ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
+ return
+ }
+
+ if repo.IsMirror {
+ ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
+ return
+ }
+
+ msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "RenameBranch", err)
+ return
+ }
+ if msg == "target_exist" {
+ ctx.Error(http.StatusUnprocessableEntity, "", "Cannot rename a branch using the same name or rename to a branch that already exists.")
+ return
+ }
+ if msg == "from_not_exist" {
+ ctx.Error(http.StatusNotFound, "", "Branch doesn't exist.")
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
// GetBranchProtection gets a branch protection
func GetBranchProtection(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go
index 38e5330b3ac..1678bc033c6 100644
--- a/routers/api/v1/repo/compare.go
+++ b/routers/api/v1/repo/compare.go
@@ -64,22 +64,19 @@ func CompareDiff(ctx *context.APIContext) {
}
}
- _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{
- Base: infos[0],
- Head: infos[1],
- })
+ compareResult, closer := parseCompareInfo(ctx, api.CreatePullRequestOption{Base: infos[0], Head: infos[1]})
if ctx.Written() {
return
}
- defer headGitRepo.Close()
+ defer closer()
verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
files := ctx.FormString("files") == "" || ctx.FormBool("files")
- apiCommits := make([]*api.Commit, 0, len(ci.Commits))
+ apiCommits := make([]*api.Commit, 0, len(compareResult.compareInfo.Commits))
userCache := make(map[string]*user_model.User)
- for i := 0; i < len(ci.Commits); i++ {
- apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache,
+ for i := 0; i < len(compareResult.compareInfo.Commits); i++ {
+ apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, compareResult.compareInfo.Commits[i], userCache,
convert.ToCommitOptions{
Stat: true,
Verification: verification,
@@ -93,7 +90,7 @@ func CompareDiff(ctx *context.APIContext) {
}
ctx.JSON(http.StatusOK, &api.Compare{
- TotalCommits: len(ci.Commits),
+ TotalCommits: len(compareResult.compareInfo.Commits),
Commits: apiCommits,
})
}
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 86b204f51e2..6f4f3efaa10 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -389,8 +389,7 @@ func CreatePullRequest(ctx *context.APIContext) {
form := *web.GetForm(ctx).(*api.CreatePullRequestOption)
if form.Head == form.Base {
- ctx.Error(http.StatusUnprocessableEntity, "BaseHeadSame",
- "Invalid PullRequest: There are no changes between the head and the base")
+ ctx.Error(http.StatusUnprocessableEntity, "BaseHeadSame", "Invalid PullRequest: There are no changes between the head and the base")
return
}
@@ -401,14 +400,22 @@ func CreatePullRequest(ctx *context.APIContext) {
)
// Get repo/branch information
- headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
+ compareResult, closer := parseCompareInfo(ctx, form)
if ctx.Written() {
return
}
- defer headGitRepo.Close()
+ defer closer()
+
+ if !compareResult.baseRef.IsBranch() || !compareResult.headRef.IsBranch() {
+ ctx.Error(http.StatusUnprocessableEntity, "BaseHeadInvalidRefType", "Invalid PullRequest: base and head must be branches")
+ return
+ }
// Check if another PR exists with the same targets
- existingPr, err := issues_model.GetUnmergedPullRequest(ctx, headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch, issues_model.PullRequestFlowGithub)
+ existingPr, err := issues_model.GetUnmergedPullRequest(ctx, compareResult.headRepo.ID, ctx.Repo.Repository.ID,
+ compareResult.headRef.ShortName(), compareResult.baseRef.ShortName(),
+ issues_model.PullRequestFlowGithub,
+ )
if err != nil {
if !issues_model.IsErrPullRequestNotExist(err) {
ctx.Error(http.StatusInternalServerError, "GetUnmergedPullRequest", err)
@@ -484,13 +491,13 @@ func CreatePullRequest(ctx *context.APIContext) {
DeadlineUnix: deadlineUnix,
}
pr := &issues_model.PullRequest{
- HeadRepoID: headRepo.ID,
+ HeadRepoID: compareResult.headRepo.ID,
BaseRepoID: repo.ID,
- HeadBranch: headBranch,
- BaseBranch: baseBranch,
- HeadRepo: headRepo,
+ HeadBranch: compareResult.headRef.ShortName(),
+ BaseBranch: compareResult.baseRef.ShortName(),
+ HeadRepo: compareResult.headRepo,
BaseRepo: repo,
- MergeBase: compareInfo.MergeBase,
+ MergeBase: compareResult.compareInfo.MergeBase,
Type: issues_model.PullRequestGitea,
}
@@ -1080,32 +1087,32 @@ func MergePullRequest(ctx *context.APIContext) {
ctx.Status(http.StatusOK)
}
-func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*repo_model.Repository, *git.Repository, *git.CompareInfo, string, string) {
- baseRepo := ctx.Repo.Repository
+type parseCompareInfoResult struct {
+ headRepo *repo_model.Repository
+ headGitRepo *git.Repository
+ compareInfo *git.CompareInfo
+ baseRef git.RefName
+ headRef git.RefName
+}
+// parseCompareInfo returns non-nil if it succeeds, it always writes to the context and returns nil if it fails
+func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (result *parseCompareInfoResult, closer func()) {
+ var err error
// Get compared branches information
// format: ...[:]
// base<-head: master...head:feature
// same repo: master...feature
+ baseRepo := ctx.Repo.Repository
+ baseRefToGuess := form.Base
- // TODO: Validate form first?
-
- baseBranch := form.Base
-
- var (
- headUser *user_model.User
- headBranch string
- isSameRepo bool
- err error
- )
-
- // If there is no head repository, it means pull request between same repository.
- headInfos := strings.Split(form.Head, ":")
- if len(headInfos) == 1 {
- isSameRepo = true
- headUser = ctx.Repo.Owner
- headBranch = headInfos[0]
+ headUser := ctx.Repo.Owner
+ headRefToGuess := form.Head
+ if headInfos := strings.Split(form.Head, ":"); len(headInfos) == 1 {
+ // If there is no head repository, it means pull request between same repository.
+ // Do nothing here because the head variables have been assigned above.
} else if len(headInfos) == 2 {
+ // There is a head repository (the head repository could also be the same base repo)
+ headRefToGuess = headInfos[1]
headUser, err = user_model.GetUserByName(ctx, headInfos[0])
if err != nil {
if user_model.IsErrUserNotExist(err) {
@@ -1113,38 +1120,29 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
} else {
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
}
- return nil, nil, nil, "", ""
+ return nil, nil
}
- headBranch = headInfos[1]
- // The head repository can also point to the same repo
- isSameRepo = ctx.Repo.Owner.ID == headUser.ID
} else {
ctx.NotFound()
- return nil, nil, nil, "", ""
+ return nil, nil
}
- ctx.Repo.PullRequest.SameRepo = isSameRepo
- log.Trace("Repo path: %q, base branch: %q, head branch: %q", ctx.Repo.GitRepo.Path, baseBranch, headBranch)
- // Check if base branch is valid.
- if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) {
- ctx.NotFound("BaseNotExist")
- return nil, nil, nil, "", ""
- }
+ isSameRepo := ctx.Repo.Owner.ID == headUser.ID
// Check if current user has fork of repository or in the same repository.
headRepo := repo_model.GetForkedRepo(ctx, headUser.ID, baseRepo.ID)
if headRepo == nil && !isSameRepo {
- err := baseRepo.GetBaseRepo(ctx)
+ err = baseRepo.GetBaseRepo(ctx)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBaseRepo", err)
- return nil, nil, nil, "", ""
+ return nil, nil
}
// Check if baseRepo's base repository is the same as headUser's repository.
if baseRepo.BaseRepo == nil || baseRepo.BaseRepo.OwnerID != headUser.ID {
log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
ctx.NotFound("GetBaseRepo")
- return nil, nil, nil, "", ""
+ return nil, nil
}
// Assign headRepo so it can be used below.
headRepo = baseRepo.BaseRepo
@@ -1154,67 +1152,68 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
if isSameRepo {
headRepo = ctx.Repo.Repository
headGitRepo = ctx.Repo.GitRepo
+ closer = func() {} // no need to close the head repo because it shares the base repo
} else {
headGitRepo, err = gitrepo.OpenRepository(ctx, headRepo)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
- return nil, nil, nil, "", ""
+ return nil, nil
}
+ closer = func() { _ = headGitRepo.Close() }
}
+ defer func() {
+ if result == nil && !isSameRepo {
+ _ = headGitRepo.Close()
+ }
+ }()
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
permBase, err := access_model.GetUserRepoPermission(ctx, baseRepo, ctx.Doer)
if err != nil {
- headGitRepo.Close()
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
- return nil, nil, nil, "", ""
- }
- if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(unit.TypeCode) {
- if log.IsTrace() {
- log.Trace("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\nUser in baseRepo has Permissions: %-+v",
- ctx.Doer,
- baseRepo,
- permBase)
- }
- headGitRepo.Close()
- ctx.NotFound("Can't read pulls or can't read UnitTypeCode")
- return nil, nil, nil, "", ""
+ return nil, nil
}
- // user should have permission to read headrepo's codes
+ if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(unit.TypeCode) {
+ log.Trace("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\nUser in baseRepo has Permissions: %-+v", ctx.Doer, baseRepo, permBase)
+ ctx.NotFound("Can't read pulls or can't read UnitTypeCode")
+ return nil, nil
+ }
+
+ // user should have permission to read headRepo's codes
+ // TODO: could the logic be simplified if the headRepo is the same as the baseRepo? Need to think more about it.
permHead, err := access_model.GetUserRepoPermission(ctx, headRepo, ctx.Doer)
if err != nil {
- headGitRepo.Close()
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
- return nil, nil, nil, "", ""
+ return nil, nil
}
if !permHead.CanRead(unit.TypeCode) {
- if log.IsTrace() {
- log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
- ctx.Doer,
- headRepo,
- permHead)
- }
- headGitRepo.Close()
+ log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v", ctx.Doer, headRepo, permHead)
ctx.NotFound("Can't read headRepo UnitTypeCode")
- return nil, nil, nil, "", ""
+ return nil, nil
}
- // Check if head branch is valid.
- if !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) {
- headGitRepo.Close()
+ baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefToGuess)
+ headRef := headGitRepo.UnstableGuessRefByShortName(headRefToGuess)
+
+ log.Trace("Repo path: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.GitRepo.Path, baseRefToGuess, baseRef, headRefToGuess, headRef)
+
+ baseRefValid := baseRef.IsBranch() || baseRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName), baseRef.ShortName())
+ headRefValid := headRef.IsBranch() || headRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(headRepo.ObjectFormatName), headRef.ShortName())
+ // Check if base&head ref are valid.
+ if !baseRefValid || !headRefValid {
ctx.NotFound()
- return nil, nil, nil, "", ""
+ return nil, nil
}
- compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch, false, false)
+ compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseRef.ShortName(), headRef.ShortName(), false, false)
if err != nil {
- headGitRepo.Close()
ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
- return nil, nil, nil, "", ""
+ return nil, nil
}
- return headRepo, headGitRepo, compareInfo, baseBranch, headBranch
+ result = &parseCompareInfoResult{headRepo: headRepo, headGitRepo: headGitRepo, compareInfo: compareInfo, baseRef: baseRef, headRef: headRef}
+ return result, closer
}
// UpdatePullRequest merge PR's baseBranch into headBranch
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 39c98c666e5..125605d98f5 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -90,6 +90,8 @@ type swaggerParameterBodies struct {
// in:body
EditRepoOption api.EditRepoOption
// in:body
+ UpdateBranchRepoOption api.UpdateBranchRepoOption
+ // in:body
TransferRepoOption api.TransferRepoOption
// in:body
CreateForkOption api.CreateForkOption
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index ad16b9fb4e4..7ed37ea26b2 100644
--- a/routers/web/repo/actions/actions.go
+++ b/routers/web/repo/actions/actions.go
@@ -245,6 +245,10 @@ func List(ctx *context.Context) {
return
}
+ if err := loadIsRefDeleted(ctx, runs); err != nil {
+ log.Error("LoadIsRefDeleted", err)
+ }
+
ctx.Data["Runs"] = runs
actors, err := actions_model.GetActors(ctx, ctx.Repo.Repository.ID)
@@ -267,6 +271,34 @@ func List(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplListActions)
}
+// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.
+// TODO: move this function to models/actions/run_list.go but now it will result in a circular import.
+func loadIsRefDeleted(ctx *context.Context, runs actions_model.RunList) error {
+ branches := make(container.Set[string], len(runs))
+ for _, run := range runs {
+ refName := git.RefName(run.Ref)
+ if refName.IsBranch() {
+ branches.Add(refName.ShortName())
+ }
+ }
+ if len(branches) == 0 {
+ return nil
+ }
+
+ branchInfos, err := git_model.GetBranches(ctx, ctx.Repo.Repository.ID, branches.Values(), false)
+ if err != nil {
+ return err
+ }
+ branchSet := git_model.BranchesToNamesSet(branchInfos)
+ for _, run := range runs {
+ refName := git.RefName(run.Ref)
+ if refName.IsBranch() && !branchSet.Contains(run.Ref) {
+ run.IsRefDeleted = true
+ }
+ }
+ return nil
+}
+
type WorkflowDispatchInput struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 73c6e54fbf5..b711038da06 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -19,6 +19,7 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
+ git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
@@ -136,8 +137,9 @@ type ViewUser struct {
}
type ViewBranch struct {
- Name string `json:"name"`
- Link string `json:"link"`
+ Name string `json:"name"`
+ Link string `json:"link"`
+ IsDeleted bool `json:"isDeleted"`
}
type ViewJobStep struct {
@@ -236,6 +238,16 @@ func ViewPost(ctx *context_module.Context) {
Name: run.PrettyRef(),
Link: run.RefLink(),
}
+ refName := git.RefName(run.Ref)
+ if refName.IsBranch() {
+ b, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, refName.ShortName())
+ if err != nil && !git_model.IsErrBranchNotExist(err) {
+ log.Error("GetBranch: %v", err)
+ } else if git_model.IsErrBranchNotExist(err) || (b != nil && b.IsDeleted) {
+ branch.IsDeleted = true
+ }
+ }
+
resp.State.Run.Commit = ViewCommit{
ShortSha: base.ShortSha(run.CommitSHA),
Link: fmt.Sprintf("%s/commit/%s", run.Repo.Link(), run.CommitSHA),
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 833f59981b4..5397411b59c 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -9,7 +9,6 @@ import (
"fmt"
"html/template"
"net/http"
- "net/url"
"strconv"
"strings"
@@ -114,7 +113,6 @@ func MustAllowPulls(ctx *context.Context) {
// User can send pull request if owns a forked repository.
if ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) {
ctx.Repo.PullRequest.Allowed = true
- ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.Doer.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
}
}
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index e30129bb44c..717d7cbce1d 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/indexer/code"
+ issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/indexer/stats"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@@ -905,6 +906,9 @@ func SettingsPost(ctx *context.Context) {
log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
}
+ // update issue indexer
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+
ctx.Flash.Success(ctx.Tr("repo.settings.archive.success"))
log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
@@ -929,6 +933,9 @@ func SettingsPost(ctx *context.Context) {
}
}
+ // update issue indexer
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+
ctx.Flash.Success(ctx.Tr("repo.settings.unarchive.success"))
log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go
index e0539f53b0c..b318c4a621a 100644
--- a/routers/web/repo/view_home.go
+++ b/routers/web/repo/view_home.go
@@ -74,9 +74,9 @@ func prepareOpenWithEditorApps(ctx *context.Context) {
schema, _, _ := strings.Cut(app.OpenURL, ":")
var iconHTML template.HTML
if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" {
- iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16, "tw-mr-2")
+ iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16)
} else {
- iconHTML = svg.RenderHTML("gitea-git", 16, "tw-mr-2") // TODO: it could support user's customized icon in the future
+ iconHTML = svg.RenderHTML("gitea-git", 16) // TODO: it could support user's customized icon in the future
}
tmplApps = append(tmplApps, map[string]any{
"DisplayName": app.DisplayName,
diff --git a/services/context/repo.go b/services/context/repo.go
index cf328ca97b7..9b544391103 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -39,10 +39,9 @@ import (
// PullRequest contains information to make a pull request
type PullRequest struct {
- BaseRepo *repo_model.Repository
- Allowed bool
- SameRepo bool
- HeadInfoSubURL string // [:] url segment
+ BaseRepo *repo_model.Repository
+ Allowed bool // it only used by the web tmpl: "PullRequestCtx.Allowed"
+ SameRepo bool // it only used by the web tmpl: "PullRequestCtx.SameRepo"
}
// Repository contains information to operate a repository
@@ -401,6 +400,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
// RepoAssignment returns a middleware to handle repository assignment
func RepoAssignment(ctx *Context) context.CancelFunc {
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
+ // FIXME: it should panic in dev/test modes to have a clear behavior
log.Trace("RepoAssignment was exec already, skipping second call ...")
return nil
}
@@ -697,7 +697,6 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
ctx.Data["BaseRepo"] = repo.BaseRepo
ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
ctx.Repo.PullRequest.Allowed = canPush
- ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.Repo.Owner.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
} else if repo.AllowsPulls(ctx) {
// Or, this is repository accepts pull requests between branches.
canCompare = true
@@ -705,7 +704,6 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
ctx.Repo.PullRequest.BaseRepo = repo
ctx.Repo.PullRequest.Allowed = canPush
ctx.Repo.PullRequest.SameRepo = true
- ctx.Repo.PullRequest.HeadInfoSubURL = util.PathEscapeSegments(ctx.Repo.BranchName)
}
ctx.Data["CanCompareOrPull"] = canCompare
ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
@@ -771,20 +769,6 @@ func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool
return ""
}
-func isStringLikelyCommitID(objFmt git.ObjectFormat, s string, minLength ...int) bool {
- minLen := util.OptionalArg(minLength, objFmt.FullLength())
- if len(s) < minLen || len(s) > objFmt.FullLength() {
- return false
- }
- for _, c := range s {
- isHex := (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
- if !isHex {
- return false
- }
- }
- return true
-}
-
func getRefNameLegacy(ctx *Base, repo *Repository, optionalExtraRef ...string) (string, RepoRefType) {
extraRef := util.OptionalArg(optionalExtraRef)
reqPath := ctx.PathParam("*")
@@ -799,7 +783,7 @@ func getRefNameLegacy(ctx *Base, repo *Repository, optionalExtraRef ...string) (
// For legacy support only full commit sha
parts := strings.Split(reqPath, "/")
- if isStringLikelyCommitID(git.ObjectFormatFromName(repo.Repository.ObjectFormatName), parts[0]) {
+ if git.IsStringLikelyCommitID(git.ObjectFormatFromName(repo.Repository.ObjectFormatName), parts[0]) {
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0], RepoRefCommit
@@ -849,7 +833,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
return getRefNameFromPath(repo, path, repo.GitRepo.IsTagExist)
case RepoRefCommit:
parts := strings.Split(path, "/")
- if isStringLikelyCommitID(repo.GetObjectFormat(), parts[0], 7) {
+ if git.IsStringLikelyCommitID(repo.GetObjectFormat(), parts[0], 7) {
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0]
@@ -985,7 +969,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
return cancel
}
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if isStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) {
+ } else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) {
ctx.Repo.IsViewCommit = true
ctx.Repo.CommitID = refName
diff --git a/services/feed/notifier.go b/services/feed/notifier.go
index a8820aeb777..d941027c352 100644
--- a/services/feed/notifier.go
+++ b/services/feed/notifier.go
@@ -417,6 +417,12 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model
}
func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
+ // ignore pull sync message for pull requests refs
+ // TODO: it's better to have a UI to let users chose
+ if refFullName.IsPull() {
+ return
+ }
+
if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
@@ -431,6 +437,12 @@ func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.Use
}
func (a *actionNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
+ // ignore pull sync message for pull requests refs
+ // TODO: it's better to have a UI to let users chose
+ if refFullName.IsPull() {
+ return
+ }
+
if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
diff --git a/services/repository/branch.go b/services/repository/branch.go
index 508817c83e6..3a95aab2649 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -305,7 +305,7 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames,
}
return db.WithTx(ctx, func(ctx context.Context) error {
- branches, err := git_model.GetBranches(ctx, repoID, branchNames)
+ branches, err := git_model.GetBranches(ctx, repoID, branchNames, true)
if err != nil {
return fmt.Errorf("git_model.GetBranches: %v", err)
}
diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl
index 5537e9e6172..fa1adb3e3ba 100644
--- a/templates/repo/actions/runs_list.tmpl
+++ b/templates/repo/actions/runs_list.tmpl
@@ -27,10 +27,10 @@
- {{if .RefLink}}
- {{.PrettyRef}}
+ {{if .IsRefDeleted}}
+ {{.PrettyRef}}
{{else}}
- {{.PrettyRef}}
+ {{.PrettyRef}}
{{end}}
{{end}}
- {{template "repo/clone_script" .}}
diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl
index 91952c8a06d..03b7a561daa 100644
--- a/templates/repo/clone_buttons.tmpl
+++ b/templates/repo/clone_buttons.tmpl
@@ -1,15 +1,13 @@
-
-{{if $.CloneButtonShowHTTPS}}
-
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 46d0398c210..4e6d375b517 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -102,27 +102,10 @@
{{end}}
- {{/* by default, the row-right flex grows, but on non-root tree path, it should not because the row-left might contain a long path */}}
-
diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl
index ceaaebc4d54..dd4c7617ce1 100644
--- a/templates/repo/issue/new_form.tmpl
+++ b/templates/repo/issue/new_form.tmpl
@@ -47,7 +47,8 @@
- {{template "repo/issue/branch_selector_field" $}}
+ {{template "repo/issue/branch_selector_field" $}}{{/* TODO: RemoveIssueRef: template "repo/issue/branch_selector_field" $*/}}
+
{{if .PageIsComparePull}}
{{template "repo/issue/sidebar/reviewer_list" $.IssuePageMetaData}}
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 02f5d3e2df9..987a882be7b 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -1,5 +1,5 @@
- {{template "repo/issue/branch_selector_field" $}}
+ {{template "repo/issue/branch_selector_field" $}}{{/* TODO: RemoveIssueRef: template "repo/issue/branch_selector_field" $*/}}
{{if .Issue.IsPull}}
{{template "repo/issue/sidebar/reviewer_list" $.IssuePageMetaData}}
diff --git a/templates/repo/latest_commit.tmpl b/templates/repo/latest_commit.tmpl
index 9d718d7197a..34a5df8f776 100644
--- a/templates/repo/latest_commit.tmpl
+++ b/templates/repo/latest_commit.tmpl
@@ -1,5 +1,5 @@
{{if not .LatestCommit}}
- …
+ …
{{else}}
{{if .LatestCommitUser}}
{{ctx.AvatarUtils.Avatar .LatestCommitUser 24 "tw-mr-1"}}
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index 3edfbb34745..0fdb45e574c 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -1,73 +1,57 @@
-
-
-
-
-
-
- {{template "repo/latest_commit" .}}
-
-
-
- {{if .LatestCommit}}{{if .LatestCommit.Committer}}{{DateUtils.TimeSince .LatestCommit.Committer.When}}{{end}}{{end}}
-
-
-
- {{if .HasParentPath}}
-
- {{svg "octicon-reply"}}..
-
- {{end}}
- {{range $item := .Files}}
+{{/* use grid layout, still use the old ID because there are many other CSS styles depending on this ID */}}
+
+
+ {{template "repo/latest_commit" .}}
+ {{if and .LatestCommit .LatestCommit.Committer}}{{DateUtils.TimeSince .LatestCommit.Committer.When}}{{end}}
+
+ {{if .HasParentPath}}
+
+ {{svg "octicon-reply"}} ..
+
+ {{end}}
+ {{range $item := .Files}}
+
{{$entry := $item.Entry}}
{{$commit := $item.Commit}}
{{$subModuleFile := $item.SubModuleFile}}
-
-
-
- {{if $entry.IsSubModule}}
- {{svg "octicon-file-submodule"}}
- {{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{/* FIXME: the usage of AppUrl seems incorrect, it would be fixed in the future, use AppSubUrl instead */}}
- {{if $refURL}}
- {{$entry.Name}}@{{ShortSha $subModuleFile.RefID}}
+
+ {{if $entry.IsSubModule}}
+ {{svg "octicon-file-submodule"}}
+ {{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{/* FIXME: the usage of AppUrl seems incorrect, it would be fixed in the future, use AppSubUrl instead */}}
+ {{if $refURL}}
+ {{$entry.Name}}@{{ShortSha $subModuleFile.RefID}}
+ {{else}}
+ {{$entry.Name}}@{{ShortSha $subModuleFile.RefID}}
+ {{end}}
+ {{else}}
+ {{if $entry.IsDir}}
+ {{$subJumpablePathName := $entry.GetSubJumpablePathName}}
+ {{svg "octicon-file-directory-fill"}}
+
+ {{$subJumpablePathFields := StringUtils.Split $subJumpablePathName "/"}}
+ {{$subJumpablePathFieldLast := (Eval (len $subJumpablePathFields) "-" 1)}}
+ {{if eq $subJumpablePathFieldLast 0}}
+ {{$subJumpablePathName}}
{{else}}
- {{$entry.Name}}@{{ShortSha $subModuleFile.RefID}}
+ {{$subJumpablePathPrefixes := slice $subJumpablePathFields 0 $subJumpablePathFieldLast}}
+ {{StringUtils.Join $subJumpablePathPrefixes "/"}}/{{index $subJumpablePathFields $subJumpablePathFieldLast}}
{{end}}
- {{else}}
- {{if $entry.IsDir}}
- {{$subJumpablePathName := $entry.GetSubJumpablePathName}}
- {{svg "octicon-file-directory-fill"}}
-
- {{$subJumpablePathFields := StringUtils.Split $subJumpablePathName "/"}}
- {{$subJumpablePathFieldLast := (Eval (len $subJumpablePathFields) "-" 1)}}
- {{if eq $subJumpablePathFieldLast 0}}
- {{$subJumpablePathName}}
- {{else}}
- {{$subJumpablePathPrefixes := slice $subJumpablePathFields 0 $subJumpablePathFieldLast}}
- {{StringUtils.Join $subJumpablePathPrefixes "/"}}/{{index $subJumpablePathFields $subJumpablePathFieldLast}}
- {{end}}
-
- {{else}}
- {{svg (printf "octicon-%s" (EntryIcon $entry))}}
- {{$entry.Name}}
- {{end}}
- {{end}}
-
-
-
- {{if $commit}}{{DateUtils.TimeSince $commit.Committer.When}}{{end}}
-
- {{end}}
-
-
-{{if and .ReadmeExist (or .IsMarkup .IsPlainText)}}
- {{template "repo/view_file" .}}
-{{end}}
+
+ {{else}}
+ {{svg (printf "octicon-%s" (EntryIcon $entry))}}
+ {{$entry.Name}}
+ {{end}}
+ {{end}}
+
+
+ {{if $commit}}{{DateUtils.TimeSince $commit.Committer.When}}{{end}}
+
+ {{end}}
+
diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl
index 045cc41d81e..ca8954928d2 100644
--- a/templates/repo/wiki/revision.tmpl
+++ b/templates/repo/wiki/revision.tmpl
@@ -15,10 +15,7 @@
-
- {{template "repo/clone_buttons" .}}
- {{template "repo/clone_script" .}}
-
+ {{template "repo/clone_panel" .}}
{{ctx.Locale.Tr "repo.wiki.wiki_page_revisions"}}
diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl
index c8e0b4254ca..843a977e3e5 100644
--- a/templates/repo/wiki/view.tmpl
+++ b/templates/repo/wiki/view.tmpl
@@ -4,7 +4,7 @@
{{$title := .title}}
+
diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl
index fe5184e7d22..a2b802f2a2d 100644
--- a/templates/shared/issuelist.tmpl
+++ b/templates/shared/issuelist.tmpl
@@ -99,7 +99,7 @@
{{.Project.Title}}
{{end}}
- {{if .Ref}}
+ {{if .Ref}}{{/* TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl" */}}
{{svg "octicon-git-branch" 14}}
{{index $.IssueRefEndNames .ID}}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index c06c0ad1541..82a301da2fe 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -5045,6 +5045,63 @@
"$ref": "#/responses/repoArchivedError"
}
}
+ },
+ "patch": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Update a branch",
+ "operationId": "repoUpdateBranch",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the branch",
+ "name": "branch",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/UpdateBranchRepoOption"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/empty"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ }
+ }
}
},
"/repos/{owner}/{repo}/collaborators": {
@@ -24968,6 +25025,22 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "UpdateBranchRepoOption": {
+ "description": "UpdateBranchRepoOption options when updating a branch in a repository",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "description": "New branch name",
+ "type": "string",
+ "uniqueItems": true,
+ "x-go-name": "Name"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"UpdateFileOptions": {
"description": "UpdateFileOptions options for updating files\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)",
"type": "object",
diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go
index 8e49516aa72..24a041de17e 100644
--- a/tests/integration/api_branch_test.go
+++ b/tests/integration/api_branch_test.go
@@ -5,6 +5,7 @@ package integration
import (
"net/http"
+ "net/http/httptest"
"net/url"
"testing"
@@ -186,6 +187,37 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran
return resp.Result().StatusCode == status
}
+func TestAPIUpdateBranch(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, _ *url.URL) {
+ t.Run("UpdateBranchWithEmptyRepo", func(t *testing.T) {
+ testAPIUpdateBranch(t, "user10", "repo6", "master", "test", http.StatusNotFound)
+ })
+ t.Run("UpdateBranchWithSameBranchNames", func(t *testing.T) {
+ resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "master", http.StatusUnprocessableEntity)
+ assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.")
+ })
+ t.Run("UpdateBranchThatAlreadyExists", func(t *testing.T) {
+ resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity)
+ assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.")
+ })
+ t.Run("UpdateBranchWithNonExistentBranch", func(t *testing.T) {
+ resp := testAPIUpdateBranch(t, "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound)
+ assert.Contains(t, resp.Body.String(), "Branch doesn't exist.")
+ })
+ t.Run("RenameBranchNormalScenario", func(t *testing.T) {
+ testAPIUpdateBranch(t, "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent)
+ })
+ })
+}
+
+func testAPIUpdateBranch(t *testing.T, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder {
+ token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteRepository)
+ req := NewRequestWithJSON(t, "PATCH", "api/v1/repos/"+ownerName+"/"+repoName+"/branches/"+from, &api.UpdateBranchRepoOption{
+ Name: to,
+ }).AddTokenAuth(token)
+ return MakeRequest(t, req, expectedHTTPStatus)
+}
+
func TestAPIBranchProtection(t *testing.T) {
defer tests.PrepareTestEnv(t)()
diff --git a/tests/integration/api_packages_cran_test.go b/tests/integration/api_packages_cran_test.go
index d307e87d4e0..667ba0908ce 100644
--- a/tests/integration/api_packages_cran_test.go
+++ b/tests/integration/api_packages_cran_test.go
@@ -115,6 +115,14 @@ func TestPackageCran(t *testing.T) {
MakeRequest(t, req, http.StatusOK)
})
+ t.Run("DownloadArchived", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", fmt.Sprintf("%s/src/contrib/Archive/%s/%s_%s.tar.gz", url, packageName, packageName, packageVersion)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusOK)
+ })
+
t.Run("Enumerate", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go
index f3188eb49f6..9565e4d2090 100644
--- a/tests/integration/api_repo_compare_test.go
+++ b/tests/integration/api_repo_compare_test.go
@@ -24,15 +24,27 @@ func TestAPICompareBranches(t *testing.T) {
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
- repoName := "repo20"
+ t.Run("CompareBranches", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo20/compare/add-csv...remove-files-b").AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
- req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/add-csv...remove-files-b", repoName).
- AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusOK)
+ var apiResp *api.Compare
+ DecodeJSON(t, resp, &apiResp)
- var apiResp *api.Compare
- DecodeJSON(t, resp, &apiResp)
+ assert.Equal(t, 2, apiResp.TotalCommits)
+ assert.Len(t, apiResp.Commits, 2)
+ })
- assert.Equal(t, 2, apiResp.TotalCommits)
- assert.Len(t, apiResp.Commits, 2)
+ t.Run("CompareCommits", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo20/compare/808038d2f71b0ab02099...c8e31bc7688741a5287f").AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ var apiResp *api.Compare
+ DecodeJSON(t, resp, &apiResp)
+
+ assert.Equal(t, 1, apiResp.TotalCommits)
+ assert.Len(t, apiResp.Commits, 1)
+ })
}
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index 4338e196174..6b1b6b8b21a 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -95,6 +95,11 @@ func TestMain(m *testing.M) {
os.Unsetenv("GIT_COMMITTER_EMAIL")
os.Unsetenv("GIT_COMMITTER_DATE")
+ // Avoid loading the default system config. On MacOS, this config
+ // sets the osxkeychain credential helper, which will cause tests
+ // to freeze with a dialog.
+ os.Setenv("GIT_CONFIG_NOSYSTEM", "true")
+
err := unittest.InitFixtures(
unittest.FixturesOptions{
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
@@ -440,7 +445,7 @@ func DecodeJSON(t testing.TB, resp *httptest.ResponseRecorder, v any) {
t.Helper()
decoder := json.NewDecoder(resp.Body)
- assert.NoError(t, decoder.Decode(v))
+ require.NoError(t, decoder.Decode(v))
}
func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile string) {
diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go
index b967ccad1ec..7889dfaf3b7 100644
--- a/tests/integration/repo_test.go
+++ b/tests/integration/repo_test.go
@@ -49,7 +49,7 @@ func testViewRepo(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- files := htmlDoc.doc.Find("#repo-files-table > TBODY > TR")
+ files := htmlDoc.doc.Find("#repo-files-table .repo-file-item")
type file struct {
fileName string
@@ -61,7 +61,7 @@ func testViewRepo(t *testing.T) {
var items []file
files.Each(func(i int, s *goquery.Selection) {
- tds := s.Find("td")
+ tds := s.Find(".repo-file-cell")
var f file
tds.Each(func(i int, s *goquery.Selection) {
if i == 0 {
@@ -127,10 +127,10 @@ func TestViewRepo1CloneLinkAnonymous(t *testing.T) {
resp := MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- link, exists := htmlDoc.doc.Find("#repo-clone-https").Attr("data-link")
+ link, exists := htmlDoc.doc.Find(".repo-clone-https").Attr("data-link")
assert.True(t, exists, "The template has changed")
assert.Equal(t, setting.AppURL+"user2/repo1.git", link)
- _, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link")
+ _, exists = htmlDoc.doc.Find(".repo-clone-ssh").Attr("data-link")
assert.False(t, exists)
}
@@ -143,10 +143,10 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- link, exists := htmlDoc.doc.Find("#repo-clone-https").Attr("data-link")
+ link, exists := htmlDoc.doc.Find(".repo-clone-https").Attr("data-link")
assert.True(t, exists, "The template has changed")
assert.Equal(t, setting.AppURL+"user2/repo1.git", link)
- link, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link")
+ link, exists = htmlDoc.doc.Find(".repo-clone-ssh").Attr("data-link")
assert.True(t, exists, "The template has changed")
sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.SSH.User, setting.SSH.Domain, setting.SSH.Port)
assert.Equal(t, sshURL, link)
@@ -161,7 +161,7 @@ func TestViewRepoWithSymlinks(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- files := htmlDoc.doc.Find("#repo-files-table > TBODY > TR > TD.name > SPAN.truncate")
+ files := htmlDoc.doc.Find("#repo-files-table .repo-file-cell.name")
items := files.Map(func(i int, s *goquery.Selection) string {
cls, _ := s.Find("SVG").Attr("class")
file := strings.Trim(s.Find("A").Text(), " \t\n")
diff --git a/tools/generate-images.js b/tools/generate-images.js
index 0bd3af29e4c..d28e0916f73 100755
--- a/tools/generate-images.js
+++ b/tools/generate-images.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
-import imageminZopfli from 'imagemin-zopfli'; // eslint-disable-line i/no-unresolved
-import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line i/no-unresolved
+import imageminZopfli from 'imagemin-zopfli'; // eslint-disable-line import-x/no-unresolved
+import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line import-x/no-unresolved
import {optimize} from 'svgo';
import {readFile, writeFile} from 'node:fs/promises';
import {argv, exit} from 'node:process';
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 158ae42d3e3..02513aebc1c 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -66,7 +66,10 @@
@import "./repo/wiki.css";
@import "./repo/header.css";
@import "./repo/home.css";
+@import "./repo/home-file-list.css";
@import "./repo/reactions.css";
+@import "./repo/clone.css";
+@import "./repo/commit-sign.css";
@import "./editor/fileeditor.css";
@import "./editor/combomarkdowneditor.css";
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index f5785c41a77..9a43e10e826 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -101,42 +101,6 @@
margin-bottom: 12px;
}
-.repository .clone-panel {
- display: flex;
- flex: 1;
-}
-
-.repository.wiki .clone-panel {
- flex: 0;
-}
-
-.repository.wiki .clone-panel input {
- width: 20ch;
-}
-
-.repository .clone-panel #repo-clone-url {
- border-radius: 0;
- flex: 1;
-}
-
-.repository .ui.action.input.clone-panel > button + button,
-.repository .ui.action.input.clone-panel > button + input {
- margin-left: -1px; /* make the borders overlap to avoid double borders */
-}
-
-.repository .clone-panel > button:first-of-type {
- border-radius: var(--border-radius) 0 0 var(--border-radius) !important;
-}
-
-.repository .clone-panel > button:last-of-type {
- border-radius: 0 var(--border-radius) var(--border-radius) 0 !important;
-}
-
-.repository .clone-panel .dropdown .menu {
- right: 0 !important;
- left: auto !important;
-}
-
.repository .repo-description {
font-size: 16px;
margin-bottom: 5px;
@@ -177,138 +141,6 @@ td .commit-summary {
overflow-wrap: anywhere;
}
-/* this is what limits the commit table width to a value that works on all viewport sizes */
-#repo-files-table th:first-of-type {
- max-width: calc(calc(min(100vw, 1280px)) - 145px - calc(2 * var(--page-margin-x)));
-}
-
-.repository.file.list #repo-files-table thead th {
- font-weight: var(--font-weight-normal);
-}
-
-.repository.file.list #repo-files-table tbody .svg {
- margin-left: 3px;
- margin-right: 5px;
-}
-
-.repository.file.list #repo-files-table tbody .svg.octicon-reply {
- margin-right: 10px;
-}
-
-.repository.file.list #repo-files-table tbody .svg.octicon-file-directory-fill,
-.repository.file.list #repo-files-table tbody .svg.octicon-file-submodule {
- color: var(--color-primary);
-}
-
-.repository.file.list #repo-files-table tbody .svg.octicon-file,
-.repository.file.list #repo-files-table tbody .svg.octicon-file-symlink-file,
-.repository.file.list #repo-files-table tbody .svg.octicon-file-directory-symlink {
- color: var(--color-secondary-dark-7);
-}
-
-.repository.file.list #repo-files-table td {
- padding-top: 0;
- padding-bottom: 0;
- overflow: initial;
-}
-
-.repository.file.list #repo-files-table td.name {
- width: 33%;
- max-width: calc(100vw - 140px);
-}
-
-@media (min-width: 1201px) {
- .repository.file.list #repo-files-table td.name {
- max-width: 150px;
- }
-}
-
-@media (min-width: 992px) and (max-width: 1200px) {
- .repository.file.list #repo-files-table td.name {
- max-width: 200px;
- }
-}
-
-@media (min-width: 768px) and (max-width: 991.98px) {
- .repository.file.list #repo-files-table td.name {
- max-width: 300px;
- }
-}
-
-.repository.file.list #repo-files-table td.message {
- color: var(--color-text-light-1);
- width: 66%;
-}
-
-@media (min-width: 1201px) {
- .repository.file.list #repo-files-table td.message {
- max-width: 400px;
- }
-}
-
-@media (min-width: 992px) and (max-width: 1200px) {
- .repository.file.list #repo-files-table td.message {
- max-width: 350px;
- }
-}
-
-@media (min-width: 768px) and (max-width: 991.98px) {
- .repository.file.list #repo-files-table td.message {
- max-width: 250px;
- }
-}
-
-.repository.file.list #repo-files-table td.age {
- color: var(--color-text-light-1);
-}
-
-.repository.file.list #repo-files-table td .truncate {
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- width: 100%;
- padding-top: 8px;
- padding-bottom: 8px;
-}
-
-.repository.file.list #repo-files-table td a {
- padding-top: 8px;
- padding-bottom: 8px;
-}
-
-.repository.file.list #repo-files-table td .at {
- margin-left: 3px;
- margin-right: 3px;
-}
-
-.repository.file.list #repo-files-table td > * {
- vertical-align: middle;
-}
-
-.repository.file.list #repo-files-table td.message .isSigned {
- cursor: default;
-}
-
-.repository.file.list #repo-files-table tr:last-of-type td:first-child {
- border-bottom-left-radius: var(--border-radius);
-}
-
-.repository.file.list #repo-files-table tr:last-of-type td:last-child {
- border-bottom-right-radius: var(--border-radius);
-}
-
-.repository.file.list #repo-files-table tr:hover {
- background-color: var(--color-hover);
-}
-
-.repository.file.list #repo-files-table tr.has-parent a {
- display: inline-block;
- padding-top: 8px;
- padding-bottom: 8px;
- width: calc(100% - 1.25rem);
-}
-
.repository.file.list .non-diff-file-content .header .icon {
font-size: 1em;
}
@@ -787,47 +619,6 @@ td .commit-summary {
height: 30px !important;
}
-.singular-commit .shabox .sha.label {
- margin: 0;
- border: 1px solid var(--color-light-border);
-}
-
-.singular-commit .shabox .sha.label.isSigned.isWarning {
- border: 1px solid var(--color-red-badge);
- background: var(--color-red-badge-bg);
-}
-
-.singular-commit .shabox .sha.label.isSigned.isWarning:hover {
- background: var(--color-red-badge-hover-bg) !important;
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerified {
- border: 1px solid var(--color-green-badge);
- background: var(--color-green-badge-bg);
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerified:hover {
- background: var(--color-green-badge-hover-bg) !important;
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted {
- border: 1px solid var(--color-yellow-badge);
- background: var(--color-yellow-badge-bg);
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover {
- background: var(--color-yellow-badge-hover-bg) !important;
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched {
- border: 1px solid var(--color-orange-badge);
- background: var(--color-orange-badge-bg);
-}
-
-.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover {
- background: var(--color-orange-badge-hover-bg) !important;
-}
-
.repository.view.issue .comment-list .timeline-item.event > .commit-status-link {
float: right;
margin-right: 8px;
@@ -1162,151 +953,6 @@ td .commit-summary {
background-color: var(--color-light) !important;
}
-.repository #commits-table td.sha .sha.label,
-.repository #repo-files-table .sha.label,
-.repository #repo-file-commit-box .sha.label,
-.repository #rev-list .sha.label,
-.repository .timeline-item.commits-list .singular-commit .sha.label {
- border: 1px solid var(--color-light-border);
-}
-
-.repository #commits-table td.sha .sha.label .detail.icon,
-.repository #repo-files-table .sha.label .detail.icon,
-.repository #repo-file-commit-box .sha.label .detail.icon,
-.repository #rev-list .sha.label .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon {
- background: var(--color-light);
- margin: -6px -10px -4px 0;
- padding: 5px 4px 5px 6px;
- border-left: 1px solid var(--color-light-border);
- border-top: 0;
- border-right: 0;
- border-bottom: 0;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-}
-
-.repository #commits-table td.sha .sha.label .detail.icon .svg,
-.repository #repo-files-table .sha.label .detail.icon .svg,
-.repository #repo-file-commit-box .sha.label .detail.icon .svg,
-.repository #rev-list .sha.label .detail.icon .svg,
-.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon .svg {
- margin: 0 0.25em 0 0;
-}
-
-.repository #commits-table td.sha .sha.label .detail.icon > div,
-.repository #repo-files-table .sha.label .detail.icon > div,
-.repository #repo-file-commit-box .sha.label .detail.icon > div,
-.repository #rev-list .sha.label .detail.icon > div,
-.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon > div {
- display: flex;
- align-items: center;
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isWarning,
-.repository #repo-files-table .sha.label.isSigned.isWarning,
-.repository #repo-file-commit-box .sha.label.isSigned.isWarning,
-.repository #rev-list .sha.label.isSigned.isWarning,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning {
- border: 1px solid var(--color-red-badge);
- background: var(--color-red-badge-bg);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isWarning .detail.icon,
-.repository #repo-files-table .sha.label.isSigned.isWarning .detail.icon,
-.repository #repo-file-commit-box .sha.label.isSigned.isWarning .detail.icon,
-.repository #rev-list .sha.label.isSigned.isWarning .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning .detail.icon {
- border-left: 1px solid var(--color-red-badge);
- color: var(--color-red-badge);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isWarning:hover,
-.repository #repo-files-table .sha.label.isSigned.isWarning:hover,
-.repository #repo-file-commit-box .sha.label.isSigned.isWarning:hover,
-.repository #rev-list .sha.label.isSigned.isWarning:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning:hover {
- background: var(--color-red-badge-hover-bg) !important;
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerified,
-.repository #repo-files-table .sha.label.isSigned.isVerified,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerified,
-.repository #rev-list .sha.label.isSigned.isVerified,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified {
- border: 1px solid var(--color-green-badge);
- background: var(--color-green-badge-bg);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,
-.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerified .detail.icon,
-.repository #rev-list .sha.label.isSigned.isVerified .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified .detail.icon {
- border-left: 1px solid var(--color-green-badge);
- color: var(--color-green-badge);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,
-.repository #repo-files-table .sha.label.isSigned.isVerified:hover,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerified:hover,
-.repository #rev-list .sha.label.isSigned.isVerified:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified:hover {
- background: var(--color-green-badge-hover-bg) !important;
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted,
-.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted,
-.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted {
- border: 1px solid var(--color-yellow-badge);
- background: var(--color-yellow-badge-bg);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
-.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
-.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted .detail.icon {
- border-left: 1px solid var(--color-yellow-badge);
- color: var(--color-yellow-badge);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted:hover,
-.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted:hover,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted:hover,
-.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted:hover {
- background: var(--color-yellow-badge-hover-bg) !important;
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched,
-.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched,
-.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched {
- border: 1px solid var(--color-orange-badge);
- background: var(--color-orange-badge-bg);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
-.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
-.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched .detail.icon {
- border-left: 1px solid var(--color-orange-badge);
- color: var(--color-orange-badge);
-}
-
-.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched:hover,
-.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched:hover,
-.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched:hover,
-.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched:hover,
-.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched:hover {
- background: var(--color-orange-badge-hover-bg) !important;
-}
-
.repository .data-table {
width: 100%;
}
@@ -1615,14 +1261,6 @@ td .commit-summary {
font-weight: var(--font-weight-normal);
}
-.repository.quickstart .guide #repo-clone-url {
- border-radius: 0;
- padding: 5px 10px;
- font-size: 1.2em;
- line-height: 1.4;
- flex: 1
-}
-
.empty-placeholder {
display: flex;
flex-direction: column;
@@ -1678,92 +1316,6 @@ td .commit-summary {
padding-top: 0;
}
-.repository .ui.attached.isSigned.isWarning {
- border-left: 1px solid var(--color-error-border);
- border-right: 1px solid var(--color-error-border);
-}
-
-.repository .ui.attached.isSigned.isWarning.top,
-.repository .ui.attached.isSigned.isWarning.message {
- border-top: 1px solid var(--color-error-border);
-}
-
-.repository .ui.attached.isSigned.isWarning.message {
- box-shadow: none;
- background-color: var(--color-error-bg);
- color: var(--color-error-text);
-}
-
-.repository .ui.attached.isSigned.isWarning.message .ui.text {
- color: var(--color-error-text);
-}
-
-.repository .ui.attached.isSigned.isWarning:last-child,
-.repository .ui.attached.isSigned.isWarning.bottom {
- border-bottom: 1px solid var(--color-error-border);
-}
-
-.repository .ui.attached.isSigned.isVerified {
- border-left: 1px solid var(--color-success-border);
- border-right: 1px solid var(--color-success-border);
-}
-
-.repository .ui.attached.isSigned.isVerified.top,
-.repository .ui.attached.isSigned.isVerified.message {
- border-top: 1px solid var(--color-success-border);
-}
-
-.repository .ui.attached.isSigned.isVerified.message {
- box-shadow: none;
- background-color: var(--color-success-bg);
- color: var(--color-success-text);
-}
-
-.repository .ui.attached.isSigned.isVerified.message .pull-right {
- color: var(--color-text);
-}
-
-.repository .ui.attached.isSigned.isVerified.message .ui.text {
- color: var(--color-success-text);
-}
-
-.repository .ui.attached.isSigned.isVerified:last-child,
-.repository .ui.attached.isSigned.isVerified.bottom {
- border-bottom: 1px solid var(--color-success-border);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted,
-.repository .ui.attached.isSigned.isVerifiedUnmatched {
- border-left: 1px solid var(--color-warning-border);
- border-right: 1px solid var(--color-warning-border);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted.top,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.top,
-.repository .ui.attached.isSigned.isVerifiedUntrusted.message,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.message {
- border-top: 1px solid var(--color-warning-border);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted.message,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.message {
- box-shadow: none;
- background-color: var(--color-warning-bg);
- color: var(--color-warning-text);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted.message .ui.text,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.message .ui.text {
- color: var(--color-warning-text);
-}
-
-.repository .ui.attached.isSigned.isVerifiedUntrusted:last-child,
-.repository .ui.attached.isSigned.isVerifiedUnmatched:last-child,
-.repository .ui.attached.isSigned.isVerifiedUntrusted.bottom,
-.repository .ui.attached.isSigned.isVerifiedUnmatched.bottom {
- border-bottom: 1px solid var(--color-warning-border);
-}
-
.repository .ui.fluid.action.input .ui.search.action.input {
flex: auto;
}
@@ -1782,7 +1334,7 @@ td .commit-summary {
.repository .repository-summary .sub-menu .item {
flex: 1;
- height: 30px;
+ height: 33px; /* match search bar height, need to be improved in the future to use some consistent methods */
line-height: var(--line-height-default);
display: flex;
align-items: center;
@@ -2110,26 +1662,18 @@ td .commit-summary {
display: flex;
align-items: center;
gap: 8px;
- justify-content: space-between;
+ flex-wrap: wrap;
}
.repo-button-row-left,
.repo-button-row-right {
display: flex;
- flex: 1;
align-items: center;
gap: 0.5rem;
}
-.repo-button-row-right {
- justify-content: flex-end;
-}
-
-@media (max-width: 1200px) {
- .repository:not(.wiki) .repo-button-row {
- flex-direction: column;
- align-items: stretch;
- }
+.repo-button-row-left {
+ flex: 1;
}
.repo-button-row .button {
@@ -2143,16 +1687,6 @@ td .commit-summary {
padding-right: 22px !important; /* normal buttons have !important paddings, so we need to override it for dropdown (Add File) icons */
}
-.repo-button-row input {
- height: 30px;
-}
-
-@media (max-width: 600px) {
- .repo-button-row-left {
- flex-wrap: wrap;
- }
-}
-
tbody.commit-list {
vertical-align: baseline;
}
@@ -2178,11 +1712,6 @@ tbody.commit-list {
overflow-wrap: anywhere;
}
-/* but in the repo-files-table we cannot */
-#repo-files-table .commit-list .message-wrapper {
- display: inline-block;
-}
-
@media (max-width: 767.98px) {
tr.commit-list {
width: 100%;
@@ -2578,25 +2107,6 @@ tbody.commit-list {
}
@media (max-width: 767.98px) {
- .repository.file.list #repo-files-table .entry,
- .repository.file.list #repo-files-table .commit-list {
- align-items: center;
- display: flex !important;
- padding-top: 4px;
- padding-bottom: 4px;
- }
- .repository.file.list #repo-files-table .entry td.age,
- .repository.file.list #repo-files-table .commit-list td.age,
- .repository.file.list #repo-files-table .entry th.age,
- .repository.file.list #repo-files-table .commit-list th.age {
- margin-left: auto;
- }
- .repository.file.list #repo-files-table .entry td.message,
- .repository.file.list #repo-files-table .commit-list td.message,
- .repository.file.list #repo-files-table .entry span.commit-summary,
- .repository.file.list #repo-files-table .commit-list tr span.commit-summary {
- display: none !important;
- }
.repository.view.issue .comment-list .timeline,
.repository.view.issue .comment-list .timeline-item {
margin-left: 0;
diff --git a/web_src/css/repo/clone.css b/web_src/css/repo/clone.css
new file mode 100644
index 00000000000..3f6a1323fea
--- /dev/null
+++ b/web_src/css/repo/clone.css
@@ -0,0 +1,35 @@
+/* only used by "repo/empty.tmpl" */
+.clone-buttons-combo {
+ display: flex;
+ align-items: center;
+ flex: 1;
+}
+
+.clone-buttons-combo input {
+ border-left: none !important;
+ border-radius: 0 !important;
+ height: 30px;
+}
+
+/* used by the clone-panel popup */
+.clone-panel-field,
+.clone-panel-list {
+ margin: 10px;
+}
+
+.clone-panel-tab .item {
+ padding: 5px 10px;
+ background: none;
+}
+
+.clone-panel-tab .item.active {
+ border-bottom: 3px solid var(--color-secondary);
+}
+
+.clone-panel-tab + .divider {
+ margin: -1px 0 0;
+}
+
+.clone-panel-list .item {
+ margin: 5px 0;
+}
diff --git a/web_src/css/repo/commit-sign.css b/web_src/css/repo/commit-sign.css
new file mode 100644
index 00000000000..e7570304194
--- /dev/null
+++ b/web_src/css/repo/commit-sign.css
@@ -0,0 +1,272 @@
+
+.repository .ui.attached.isSigned.isWarning {
+ border-left: 1px solid var(--color-error-border);
+ border-right: 1px solid var(--color-error-border);
+}
+
+.repository .ui.attached.isSigned.isWarning.top,
+.repository .ui.attached.isSigned.isWarning.message {
+ border-top: 1px solid var(--color-error-border);
+}
+
+.repository .ui.attached.isSigned.isWarning.message {
+ box-shadow: none;
+ background-color: var(--color-error-bg);
+ color: var(--color-error-text);
+}
+
+.repository .ui.attached.isSigned.isWarning.message .ui.text {
+ color: var(--color-error-text);
+}
+
+.repository .ui.attached.isSigned.isWarning:last-child,
+.repository .ui.attached.isSigned.isWarning.bottom {
+ border-bottom: 1px solid var(--color-error-border);
+}
+
+.repository .ui.attached.isSigned.isVerified {
+ border-left: 1px solid var(--color-success-border);
+ border-right: 1px solid var(--color-success-border);
+}
+
+.repository .ui.attached.isSigned.isVerified.top,
+.repository .ui.attached.isSigned.isVerified.message {
+ border-top: 1px solid var(--color-success-border);
+}
+
+.repository .ui.attached.isSigned.isVerified.message {
+ box-shadow: none;
+ background-color: var(--color-success-bg);
+ color: var(--color-success-text);
+}
+
+.repository .ui.attached.isSigned.isVerified.message .pull-right {
+ color: var(--color-text);
+}
+
+.repository .ui.attached.isSigned.isVerified.message .ui.text {
+ color: var(--color-success-text);
+}
+
+.repository .ui.attached.isSigned.isVerified:last-child,
+.repository .ui.attached.isSigned.isVerified.bottom {
+ border-bottom: 1px solid var(--color-success-border);
+}
+
+.repository .ui.attached.isSigned.isVerifiedUntrusted,
+.repository .ui.attached.isSigned.isVerifiedUnmatched {
+ border-left: 1px solid var(--color-warning-border);
+ border-right: 1px solid var(--color-warning-border);
+}
+
+.repository .ui.attached.isSigned.isVerifiedUntrusted.top,
+.repository .ui.attached.isSigned.isVerifiedUnmatched.top,
+.repository .ui.attached.isSigned.isVerifiedUntrusted.message,
+.repository .ui.attached.isSigned.isVerifiedUnmatched.message {
+ border-top: 1px solid var(--color-warning-border);
+}
+
+.repository .ui.attached.isSigned.isVerifiedUntrusted.message,
+.repository .ui.attached.isSigned.isVerifiedUnmatched.message {
+ box-shadow: none;
+ background-color: var(--color-warning-bg);
+ color: var(--color-warning-text);
+}
+
+.repository .ui.attached.isSigned.isVerifiedUntrusted.message .ui.text,
+.repository .ui.attached.isSigned.isVerifiedUnmatched.message .ui.text {
+ color: var(--color-warning-text);
+}
+
+.repository .ui.attached.isSigned.isVerifiedUntrusted:last-child,
+.repository .ui.attached.isSigned.isVerifiedUnmatched:last-child,
+.repository .ui.attached.isSigned.isVerifiedUntrusted.bottom,
+.repository .ui.attached.isSigned.isVerifiedUnmatched.bottom {
+ border-bottom: 1px solid var(--color-warning-border);
+}
+
+.repository #commits-table td.sha .sha.label,
+.repository #repo-files-table .sha.label,
+.repository #repo-file-commit-box .sha.label,
+.repository #rev-list .sha.label,
+.repository .timeline-item.commits-list .singular-commit .sha.label {
+ border: 1px solid var(--color-light-border);
+}
+
+.repository #commits-table td.sha .sha.label .detail.icon,
+.repository #repo-files-table .sha.label .detail.icon,
+.repository #repo-file-commit-box .sha.label .detail.icon,
+.repository #rev-list .sha.label .detail.icon,
+.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon {
+ background: var(--color-light);
+ margin: -6px -10px -4px 0;
+ padding: 5px 4px 5px 6px;
+ border-left: 1px solid var(--color-light-border);
+ border-top: 0;
+ border-right: 0;
+ border-bottom: 0;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.repository #commits-table td.sha .sha.label .detail.icon .svg,
+.repository #repo-files-table .sha.label .detail.icon .svg,
+.repository #repo-file-commit-box .sha.label .detail.icon .svg,
+.repository #rev-list .sha.label .detail.icon .svg,
+.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon .svg {
+ margin: 0 0.25em 0 0;
+}
+
+.repository #commits-table td.sha .sha.label .detail.icon > div,
+.repository #repo-files-table .sha.label .detail.icon > div,
+.repository #repo-file-commit-box .sha.label .detail.icon > div,
+.repository #rev-list .sha.label .detail.icon > div,
+.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon > div {
+ display: flex;
+ align-items: center;
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isWarning,
+.repository #repo-files-table .sha.label.isSigned.isWarning,
+.repository #repo-file-commit-box .sha.label.isSigned.isWarning,
+.repository #rev-list .sha.label.isSigned.isWarning,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning {
+ border: 1px solid var(--color-red-badge);
+ background: var(--color-red-badge-bg);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isWarning .detail.icon,
+.repository #repo-files-table .sha.label.isSigned.isWarning .detail.icon,
+.repository #repo-file-commit-box .sha.label.isSigned.isWarning .detail.icon,
+.repository #rev-list .sha.label.isSigned.isWarning .detail.icon,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning .detail.icon {
+ border-left: 1px solid var(--color-red-badge);
+ color: var(--color-red-badge);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isWarning:hover,
+.repository #repo-files-table .sha.label.isSigned.isWarning:hover,
+.repository #repo-file-commit-box .sha.label.isSigned.isWarning:hover,
+.repository #rev-list .sha.label.isSigned.isWarning:hover,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning:hover {
+ background: var(--color-red-badge-hover-bg) !important;
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerified,
+.repository #repo-files-table .sha.label.isSigned.isVerified,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerified,
+.repository #rev-list .sha.label.isSigned.isVerified,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified {
+ border: 1px solid var(--color-green-badge);
+ background: var(--color-green-badge-bg);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,
+.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerified .detail.icon,
+.repository #rev-list .sha.label.isSigned.isVerified .detail.icon,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified .detail.icon {
+ border-left: 1px solid var(--color-green-badge);
+ color: var(--color-green-badge);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,
+.repository #repo-files-table .sha.label.isSigned.isVerified:hover,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerified:hover,
+.repository #rev-list .sha.label.isSigned.isVerified:hover,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified:hover {
+ background: var(--color-green-badge-hover-bg) !important;
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted,
+.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted,
+.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted {
+ border: 1px solid var(--color-yellow-badge);
+ background: var(--color-yellow-badge-bg);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
+.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
+.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted .detail.icon,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted .detail.icon {
+ border-left: 1px solid var(--color-yellow-badge);
+ color: var(--color-yellow-badge);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted:hover,
+.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted:hover,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted:hover,
+.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted:hover,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted:hover {
+ background: var(--color-yellow-badge-hover-bg) !important;
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched,
+.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched,
+.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched {
+ border: 1px solid var(--color-orange-badge);
+ background: var(--color-orange-badge-bg);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
+.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
+.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched .detail.icon,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched .detail.icon {
+ border-left: 1px solid var(--color-orange-badge);
+ color: var(--color-orange-badge);
+}
+
+.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched:hover,
+.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched:hover,
+.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched:hover,
+.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched:hover,
+.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched:hover {
+ background: var(--color-orange-badge-hover-bg) !important;
+}
+
+.singular-commit .shabox .sha.label {
+ margin: 0;
+ border: 1px solid var(--color-light-border);
+}
+
+.singular-commit .shabox .sha.label.isSigned.isWarning {
+ border: 1px solid var(--color-red-badge);
+ background: var(--color-red-badge-bg);
+}
+
+.singular-commit .shabox .sha.label.isSigned.isWarning:hover {
+ background: var(--color-red-badge-hover-bg) !important;
+}
+
+.singular-commit .shabox .sha.label.isSigned.isVerified {
+ border: 1px solid var(--color-green-badge);
+ background: var(--color-green-badge-bg);
+}
+
+.singular-commit .shabox .sha.label.isSigned.isVerified:hover {
+ background: var(--color-green-badge-hover-bg) !important;
+}
+
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted {
+ border: 1px solid var(--color-yellow-badge);
+ background: var(--color-yellow-badge-bg);
+}
+
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover {
+ background: var(--color-yellow-badge-hover-bg) !important;
+}
+
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched {
+ border: 1px solid var(--color-orange-badge);
+ background: var(--color-orange-badge-bg);
+}
+
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover {
+ background: var(--color-orange-badge-hover-bg) !important;
+}
diff --git a/web_src/css/repo/home-file-list.css b/web_src/css/repo/home-file-list.css
new file mode 100644
index 00000000000..ecb26fa6629
--- /dev/null
+++ b/web_src/css/repo/home-file-list.css
@@ -0,0 +1,75 @@
+#repo-files-table {
+ width: 100%;
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ border: 1px solid var(--color-light-border);
+ border-radius: var(--border-radius);
+ margin: 10px 0; /* match the "clone-panel-popup" margin to avoid "visual double-border" */
+}
+
+#repo-files-table .svg.octicon-file-directory-fill,
+#repo-files-table .svg.octicon-file-submodule {
+ color: var(--color-primary);
+}
+
+#repo-files-table .svg.octicon-file,
+#repo-files-table .svg.octicon-file-symlink-file,
+#repo-files-table .svg.octicon-file-directory-symlink {
+ color: var(--color-secondary-dark-7);
+}
+
+#repo-files-table .repo-file-item {
+ display: contents;
+}
+
+#repo-files-table .repo-file-item:hover > .repo-file-cell {
+ background: var(--color-hover);
+}
+
+#repo-files-table .repo-file-line,
+#repo-files-table .repo-file-cell {
+ border-top: 1px solid var(--color-light-border);
+ padding: 6px 10px;
+}
+
+#repo-files-table .repo-file-line:first-child {
+ border-top: none;
+}
+
+#repo-files-table .repo-file-line {
+ grid-column: 1 / span 3;
+ display: flex;
+ align-items: center;
+ gap: 0.5em;
+ padding: 6px 10px;
+}
+
+#repo-files-table .repo-file-last-commit {
+ background: var(--color-box-header);
+}
+
+#repo-files-table .repo-file-cell.name {
+ max-width: 300px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#repo-files-table .repo-file-cell.message {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: var(--color-text-light-1);
+}
+
+#repo-files-table .repo-file-cell.age {
+ text-align: right;
+ white-space: nowrap;
+ color: var(--color-text-light-1);
+}
+
+@media (max-width: 767.98px) {
+ #repo-files-table .repo-file-cell.name {
+ max-width: 150px;
+ }
+}
diff --git a/web_src/css/repo/wiki.css b/web_src/css/repo/wiki.css
index ba502d32161..ca59dadb9c8 100644
--- a/web_src/css/repo/wiki.css
+++ b/web_src/css/repo/wiki.css
@@ -59,9 +59,6 @@
}
@media (max-width: 767.98px) {
- .repository.wiki .clone-panel #repo-clone-url {
- width: 160px;
- }
.repository.wiki .wiki-content-main.with-sidebar,
.repository.wiki .wiki-content-sidebar {
float: none;
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 7f647b668a6..eece2efaf86 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -429,7 +429,8 @@ export function initRepositoryActionView() {
{{ run.commit.pusher.displayName }}
- {{ run.commit.branch.name }}
+ {{ run.commit.branch.name }}
+ {{ run.commit.branch.name }}
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index 97678b9a13b..e79fc80d8e3 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -1,5 +1,6 @@
-