From e399f0f5b4661ac71bcf4f8badcd04d73b71a640 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 16 Jun 2022 11:10:33 +0200 Subject: [PATCH 01/35] Don't buffer doctor logger (#19982) - We don't need to buffer the logger with a thousand capacity. It's not a high-throughput logger, this also caused issue whereby the logger can't keep up with repeated messages being send(somehow they are lost in the queue?). - Resolves #19969 Co-authored-by: Lunny Xiao --- cmd/doctor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/doctor.go b/cmd/doctor.go index 73dfeb1dbe99a..3f16c6e2a600a 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -203,7 +203,7 @@ func runDoctor(ctx *cli.Context) error { // Now we can set up our own logger to return information about what the doctor is doing if err := log.NewNamedLogger("doctorouter", - 1000, + 0, "console", "console", fmt.Sprintf(`{"level":"INFO","stacktracelevel":"NONE","colorize":%t,"flags":-1}`, colorize)); err != nil { From a6b7c3646a91eff2ca77e019814dfb8b586cc28c Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Thu, 16 Jun 2022 20:37:13 +0800 Subject: [PATCH 02/35] fix `go to file` link for mirror repository (#19983) the `BaseRepo` not always exit, should use `Repository`. Signed-off-by: a1012112796 <1012112796@qq.com> --- templates/repo/home.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 73c6702a9072e..1a0323f5d1826 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -74,7 +74,7 @@ {{end}} From d05fb6f6c96b536567911ac077b53a0b03717f29 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Thu, 16 Jun 2022 14:10:29 +0000 Subject: [PATCH 03/35] [skip ci] Updated translations via Crowdin --- options/locale/locale_zh-TW.ini | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index cbd633d13044e..6d09a2f31af65 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -996,7 +996,7 @@ no_desc=暫無描述 quick_guide=快速幫助 clone_this_repo=Clone 此儲存庫 create_new_repo_command=從命令列建立新儲存庫。 -push_exist_repo=從命令行推送已經建立的儲存庫 +push_exist_repo=從命令列推送已存在的儲存庫 empty_message=此儲存庫未包含任何內容。 broken_message=無法讀取此儲存庫底層的 Git 資料。請聯絡此 Gitea 執行個體的管理員或刪除此儲存庫。 @@ -1974,6 +1974,7 @@ settings.event_pull_request_review=合併請求審核 settings.event_pull_request_review_desc=核准、退回或提出審核留言。 settings.event_pull_request_sync=合併請求同步 settings.event_pull_request_sync_desc=合併請求同步。 +settings.event_package=套件 settings.branch_filter=分支篩選 settings.branch_filter_desc=推送、建立分支、刪除分支事件的白名單,請使用 glob 比對模式。如果留白或輸入*,所有分支的事件都會被回報。語法參見 github.com/gobwas/glob。範例:master, {master,release*}。 settings.active=啟用 @@ -2574,6 +2575,13 @@ repos.forks=Fork 數 repos.issues=問題數 repos.size=大小 +packages.package_manage_panel=套件管理 +packages.total_size=總大小: %s +packages.owner=擁有者 +packages.creator=建立者 +packages.name=名稱 +packages.version=版本 +packages.size=大小 defaulthooks=預設 Webhook defaulthooks.desc=當觸發某些 Gitea 事件時,Webhook 會自動發出 HTTP POST 請求到指定的伺服器。這裡所定義的 Webhook 是預設的,並且會複製到所有新儲存庫。在 Webhook 指南閱讀更多內容。 @@ -3015,6 +3023,7 @@ error.no_unit_allowed_repo=您未被允許存取此儲存庫的任何區域。 error.unit_not_allowed=您未被允許訪問此儲存庫區域 [packages] +title=套件 dependency.id=ID dependency.version=版本 From 36127a3336d3e124f9ea67d4bcf4b76e9a7a07b6 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 16 Jun 2022 16:49:22 +0200 Subject: [PATCH 04/35] Don't prevent overflow on y-as (#19978) - Fomantic tries to prevent overflowing on the `y/x`-as by default on stackable menu's on mobile screens. We already solve this issue by forcing overflow on x as and hide it on y as(due to some issues with other menu's), since https://github.com/go-gitea/gitea/pull/19486. - However this edge case does require a y-overflow to show the dropdown, because you cannot easily adjust this with CSS, once you're fiddling with overflow's (https://stackoverflow.com/a/6433475). However interesting behavior is noted https://css-tricks.com/popping-hidden-overflow/ when you remove the position: relative, it will suddenly work again. Well because this is the only solution without redesigning dropdowns, I think we can live with the side-effect of the dropdown items being full-width instead "relative" width to their parent. - Resolves #19976 Co-authored-by: Lunny Xiao --- web_src/less/_base.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web_src/less/_base.less b/web_src/less/_base.less index bd5754c1e3502..78f32956efce1 100644 --- a/web_src/less/_base.less +++ b/web_src/less/_base.less @@ -2200,5 +2200,9 @@ table th[data-sortt-desc] { .item { width: initial !important; } + + > .dropdown.item { + position: initial; + } } } From 70ce051f1a7e266dccdd7cfd42f88a2570448770 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 16 Jun 2022 15:10:36 +0000 Subject: [PATCH 05/35] [skip ci] Updated translations via Crowdin --- options/locale/locale_zh-TW.ini | 88 ++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 6d09a2f31af65..efeb4cb99cb1f 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -491,7 +491,9 @@ auth_failed=授權認證失敗:%v still_own_repo=此帳戶仍然擁有一個或多個儲存庫,您必須先刪除或轉移它們。 still_has_org=此帳戶仍是一個或多個組織的成員,您必須先離開它們。 +still_own_packages=您的帳戶擁有一個或多個套件,請先刪除他們。 org_still_own_repo=該組織仍然是某些儲存庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織! +org_still_own_packages=此組織擁有一個或多個套件,請先刪除他們。 target_branch_not_exist=目標分支不存在 @@ -1012,6 +1014,7 @@ tags=標籤 issues=問題 pulls=合併請求 project_board=專案 +packages=套件 labels=標籤 org_labels_desc=組織層級標籤可用於此組織下的所有存儲庫。 org_labels_desc_manage=管理 @@ -1507,7 +1510,7 @@ pulls.nothing_to_compare_and_allow_empty_pr=這些分支的內容相同,此合 pulls.has_pull_request=`已有介於這些分支間的合併請求:%[2]s#%[3]d` pulls.create=建立合併請求 pulls.title_desc=請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s -pulls.merged_title_desc=將 %[1]d 次代碼提交從 %[2]s 合併至 %[3]s %[4]s +pulls.merged_title_desc=將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s pulls.change_target_branch_at=`將目標分支從 %s 更改為 %s %s` pulls.tab_conversation=對話內容 pulls.tab_commits=程式碼提交 @@ -1817,6 +1820,7 @@ settings.pulls.allow_manual_merge=允許將合併請求標記為手動合併 settings.pulls.enable_autodetect_manual_merge=啟用自動偵測手動合併(注意:在某些特殊情況下可能發生誤判) settings.pulls.allow_rebase_update=啟用透過 Rebase 更新合併請求分支 settings.pulls.default_delete_branch_after_merge=預設在合併後刪除合併請求分支 +settings.packages_desc=啟用儲存庫套件註冊中心 settings.projects_desc=啟用儲存庫專案 settings.admin_settings=管理員設定 settings.admin_enable_health_check=啟用儲存庫的健康檢查 (git fsck) @@ -1975,6 +1979,7 @@ settings.event_pull_request_review_desc=核准、退回或提出審核留言。 settings.event_pull_request_sync=合併請求同步 settings.event_pull_request_sync_desc=合併請求同步。 settings.event_package=套件 +settings.event_package_desc=套件已在儲存庫中建立或刪除。 settings.branch_filter=分支篩選 settings.branch_filter_desc=推送、建立分支、刪除分支事件的白名單,請使用 glob 比對模式。如果留白或輸入*,所有分支的事件都會被回報。語法參見 github.com/gobwas/glob。範例:master, {master,release*}。 settings.active=啟用 @@ -2439,7 +2444,7 @@ dashboard.cron.error=Cron 中的錯誤: %s: %[3]s dashboard.cron.finished=Cron: %[1]s 已完成 dashboard.delete_inactive_accounts=刪除所有未啟用帳戶 dashboard.delete_inactive_accounts.started=刪除所有未啟用帳戶的任務已啟動。 -dashboard.delete_repo_archives=刪除所有儲存庫存檔(ZIP, TAR.GZ, etc..) +dashboard.delete_repo_archives=刪除所有儲存庫存檔 (ZIP, TAR.GZ, etc..) dashboard.delete_repo_archives.started=刪除所有儲存庫存檔的任務已啟動。 dashboard.delete_missing_repos=刪除所有遺失 Git 檔案的儲存庫 dashboard.delete_missing_repos.started=刪除所有遺失 Git 檔案的儲存庫的任務已啟動。 @@ -2459,6 +2464,7 @@ dashboard.resync_all_hooks=重新同步所有儲存庫的 pre-receive、update dashboard.reinit_missing_repos=重新初始化所有記錄存在但遺失的 Git 儲存庫 dashboard.sync_external_users=同步外部使用者資料 dashboard.cleanup_hook_task_table=清理 hook_task 資料表 +dashboard.cleanup_packages=清理已過期的套件 dashboard.server_uptime=服務執行時間 dashboard.current_goroutine=目前的 Goroutines 數量 dashboard.current_memory_usage=目前記憶體使用量 @@ -2529,6 +2535,7 @@ users.delete_account=刪除使用者帳戶 users.cannot_delete_self=您無法刪除您自己 users.still_own_repo=這個使用者還擁有一個或更多的儲存庫。請先刪除或是轉移這些儲存庫。 users.still_has_org=此使用者是組織的成員。請先將他從組織中移除。 +users.still_own_packages=此使用者擁有一個或多個套件,請先刪除這些套件。 users.deletion_success=使用者帳戶已被刪除。 users.reset_2fa=重設兩步驟驗證 users.list_status_filter.menu_text=篩選 @@ -2581,7 +2588,10 @@ packages.owner=擁有者 packages.creator=建立者 packages.name=名稱 packages.version=版本 +packages.type=類型 +packages.repository=儲存庫 packages.size=大小 +packages.published=已發布 defaulthooks=預設 Webhook defaulthooks.desc=當觸發某些 Gitea 事件時,Webhook 會自動發出 HTTP POST 請求到指定的伺服器。這裡所定義的 Webhook 是預設的,並且會複製到所有新儲存庫。在 Webhook 指南閱讀更多內容。 @@ -3024,6 +3034,80 @@ error.unit_not_allowed=您未被允許訪問此儲存庫區域 [packages] title=套件 +desc=管理儲存庫套件。 +empty=目前還沒有套件。 +filter.type=類型 +filter.type.all=所有 +filter.no_result=沒有篩選結果。 +filter.container.tagged=已加標籤 +filter.container.untagged=未加標籤 +published_by=發布於 %[1]s 由 %[3]s +published_by_in=發布於 %[1]s 由 %[3]s%[5]s +installation=安裝 +about=關於此套件 +requirements=需求 +dependencies=相依性 +keywords=關鍵字 +details=詳情 +details.author=作者 +details.project_site=專案網站 +details.license=授權條款 +assets=檔案 +versions=版本 +versions.on=於 +versions.view_all=檢視全部 dependency.id=ID dependency.version=版本 +composer.registry=在您的 ~/.composer/config.json 檔設定此註冊中心: +composer.install=執行下列命令以使用 Composer 安裝此套件: +composer.dependencies=相依性 +composer.dependencies.development=開發相依性 +conan.details.repository=儲存庫 +conan.registry=透過下列命令設定此註冊中心: +conan.install=執行下列命令以使用 Conan 安裝此套件: +container.details.type=映像檔類型 +container.details.platform=平台 +container.details.repository_site=儲存庫網站 +container.details.documentation_site=文件網站 +container.pull=透過下列命令拉取映像檔: +container.multi_arch=作業系統 / 架構 +container.layers=映像檔 Layers +container.labels=標籤 +container.labels.key=鍵 +container.labels.value=值 +generic.download=透過下列命令下載套件: +helm.registry=透過下列命令設定此註冊中心: +helm.install=執行下列命令安裝此套件: +maven.registry=在您的 pom.xml 檔設定此註冊中心: +maven.install=若要使用此套件,請在您 pom.xml 檔的 dependencies 段落加入下列內容: +maven.install2=透過下列命令執行: +maven.download=透過下列命令下載相依性: +nuget.registry=透過下列命令設定此註冊中心: +nuget.install=執行下列命令以使用 NuGet 安裝此套件: +nuget.dependency.framework=目標框架 +npm.registry=在您的 .npmrc 檔設定此註冊中心: +npm.install=執行下列命令以使用 npm 安裝此套件: +npm.install2=或將它加到 package.json 檔: +npm.dependencies=相依性 +npm.dependencies.development=開發相依性 +npm.dependencies.peer=Peer 相依性 +npm.dependencies.optional=選用相依性 +npm.details.tag=標籤 +pypi.requires=需要 Python +pypi.install=執行下列命令以使用 pip 安裝此套件: +rubygems.install=執行下列命令以使用 gem 安裝此套件: +rubygems.install2=或將它加到 Gemfile: +rubygems.dependencies.runtime=執行階段相依性 +rubygems.dependencies.development=開發相依性 +rubygems.required.ruby=需要的 Ruby 版本 +rubygems.required.rubygems=需要的 RubyGem 版本 +settings.link=連結此套件到儲存庫 +settings.link.description=如果您將套件連結到儲存庫,該套件會顯示在儲存庫的套件清單。 +settings.link.select=選擇儲存庫 +settings.link.button=更新儲存庫連結 +settings.link.success=儲存庫連結更新成功。 +settings.link.error=儲存庫連結更新失敗。 +settings.delete=刪除套件 +settings.delete.success=已刪除該套件。 +settings.delete.error=刪除套件失敗。 From 157b4057531b99b2d3b5c086f385c830aa38354f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 16 Jun 2022 23:47:44 +0800 Subject: [PATCH 06/35] Remove legacy git code (ver < 2.0), fine tune markup tests (#19930) * clean git support for ver < 2.0 * fine tune tests for markup (which requires git module) * remove unnecessary comments * try to fix tests * try test again * use const for GitVersionRequired instead of var * try to fix integration test * Refactor CheckAttributeReader to make a *git.Repository version * update document for commit signing with Gitea's internal gitconfig * update document for commit signing with Gitea's internal gitconfig Co-authored-by: Andrew Thornton Co-authored-by: Lunny Xiao --- cmd/hook.go | 4 +- docs/content/doc/advanced/signing.en-us.md | 15 +++-- integrations/integration_test.go | 9 ++- integrations/migration-test/migration_test.go | 2 +- models/migrations/migrations_test.go | 6 +- models/unittest/testdb.go | 4 +- modules/git/commit.go | 27 +++------ modules/git/git.go | 55 +++++++++-------- modules/git/repo_attribute.go | 60 +++++++++++++------ modules/git/repo_compare.go | 8 +-- modules/git/repo_language_stats_gogit.go | 31 +--------- modules/git/repo_language_stats_nogogit.go | 33 +--------- modules/git/repo_tree.go | 4 +- modules/markup/html_test.go | 9 +++ modules/markup/markdown/markdown_test.go | 8 +++ modules/repository/init.go | 22 ++++--- services/gitdiff/gitdiff.go | 33 +--------- services/pull/merge.go | 27 +++------ services/repository/files/temp_repo.go | 49 +++++++-------- 19 files changed, 179 insertions(+), 227 deletions(-) diff --git a/cmd/hook.go b/cmd/hook.go index 8078763b183fd..73386038b375a 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -308,6 +308,8 @@ func runHookPostReceive(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() + setup("hooks/post-receive.log", c.Bool("debug")) + // First of all run update-server-info no matter what if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil { return fmt.Errorf("Failed to call 'git update-server-info': %v", err) @@ -318,8 +320,6 @@ func runHookPostReceive(c *cli.Context) error { return nil } - setup("hooks/post-receive.log", c.Bool("debug")) - if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { if setting.OnlyAllowPushIfGiteaEnvironmentSet { return fail(`Rejecting changes as Gitea environment not set. diff --git a/docs/content/doc/advanced/signing.en-us.md b/docs/content/doc/advanced/signing.en-us.md index aaaeb71034821..8ae2f94e9ece9 100644 --- a/docs/content/doc/advanced/signing.en-us.md +++ b/docs/content/doc/advanced/signing.en-us.md @@ -83,8 +83,7 @@ The first option to discuss is the `SIGNING_KEY`. There are three main options: - `none` - this prevents Gitea from signing any commits -- `default` - Gitea will default to the key configured within - `git config` +- `default` - Gitea will default to the key configured within `git config` - `KEYID` - Gitea will sign commits with the gpg key with the ID `KEYID`. In this case you should provide a `SIGNING_NAME` and `SIGNING_EMAIL` to be displayed for this key. @@ -98,6 +97,12 @@ repositories, `SIGNING_KEY=default` could be used to provide different signing keys on a per-repository basis. However, this is clearly not an ideal UI and therefore subject to change. +**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`. +If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`) +or the Gitea internal git config `{[repository].ROOT}/.gitconfig`. +Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`. + + ### `INITIAL_COMMIT` This option determines whether Gitea should sign the initial commit @@ -118,7 +123,7 @@ The possible values are: - `never`: Never sign - `pubkey`: Only sign if the user has a public key -- `twofa`: Only sign if the user logs in with two factor authentication +- `twofa`: Only sign if the user logs in with two-factor authentication - `parentsigned`: Only sign if the parent commit is signed. - `always`: Always sign @@ -132,7 +137,7 @@ editor or API CRUD actions. The possible values are: - `never`: Never sign - `pubkey`: Only sign if the user has a public key -- `twofa`: Only sign if the user logs in with two factor authentication +- `twofa`: Only sign if the user logs in with two-factor authentication - `parentsigned`: Only sign if the parent commit is signed. - `always`: Always sign @@ -146,7 +151,7 @@ The possible options are: - `never`: Never sign - `pubkey`: Only sign if the user has a public key -- `twofa`: Only sign if the user logs in with two factor authentication +- `twofa`: Only sign if the user logs in with two-factor authentication - `basesigned`: Only sign if the parent commit in the base repo is signed. - `headsigned`: Only sign if the head commit in the head branch is signed. - `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed. diff --git a/integrations/integration_test.go b/integrations/integration_test.go index ce21eb2ef7bc8..b0004927f7b57 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -174,7 +174,12 @@ func initIntegrationTest() { setting.LoadForTest() setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" _ = util.RemoveAll(repo_module.LocalCopyPath()) + + if err := git.InitOnceWithSync(context.Background()); err != nil { + log.Fatal("git.InitOnceWithSync: %v", err) + } git.CheckLFSVersion() + setting.InitDBConfig() if err := storage.Init(); err != nil { fmt.Printf("Init storage failed: %v", err) @@ -275,7 +280,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() { assert.NoError(t, unittest.LoadFixtures()) assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath)) - assert.NoError(t, git.InitOnceWithSync(context.Background())) + assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again ownerDirs, err := os.ReadDir(setting.RepoRootPath) if err != nil { assert.NoError(t, err, "unable to read the new repo root: %v\n", err) @@ -576,7 +581,7 @@ func resetFixtures(t *testing.T) { assert.NoError(t, unittest.LoadFixtures()) assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath)) - assert.NoError(t, git.InitOnceWithSync(context.Background())) + assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again ownerDirs, err := os.ReadDir(setting.RepoRootPath) if err != nil { assert.NoError(t, err, "unable to read the new repo root: %v\n", err) diff --git a/integrations/migration-test/migration_test.go b/integrations/migration-test/migration_test.go index 83c31d80187e4..20a5c903a92cc 100644 --- a/integrations/migration-test/migration_test.go +++ b/integrations/migration-test/migration_test.go @@ -62,7 +62,6 @@ func initMigrationTest(t *testing.T) func() { assert.True(t, len(setting.RepoRootPath) != 0) assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath)) - assert.NoError(t, git.InitOnceWithSync(context.Background())) ownerDirs, err := os.ReadDir(setting.RepoRootPath) if err != nil { assert.NoError(t, err, "unable to read the new repo root: %v\n", err) @@ -83,6 +82,7 @@ func initMigrationTest(t *testing.T) func() { } } + assert.NoError(t, git.InitOnceWithSync(context.Background())) git.CheckLFSVersion() setting.InitDBConfig() setting.NewLogServices(true) diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go index 0c8e74f734c56..46782f24a1003 100644 --- a/models/migrations/migrations_test.go +++ b/models/migrations/migrations_test.go @@ -66,6 +66,10 @@ func TestMain(m *testing.M) { setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() + if err = git.InitOnceWithSync(context.Background()); err != nil { + fmt.Printf("Unable to InitOnceWithSync: %v\n", err) + os.Exit(1) + } git.CheckLFSVersion() setting.InitDBConfig() setting.NewLogServices(true) @@ -203,7 +207,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En deferFn := PrintCurrentTest(t, ourSkip) assert.NoError(t, os.RemoveAll(setting.RepoRootPath)) assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath)) - assert.NoError(t, git.InitOnceWithSync(context.Background())) + assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again ownerDirs, err := os.ReadDir(setting.RepoRootPath) if err != nil { assert.NoError(t, err, "unable to read the new repo root: %v\n", err) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index d1a44985100e6..baea46dbce68f 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -117,9 +117,11 @@ func MainTest(m *testing.M, testOpts *TestOptions) { if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil { fatalTestError("util.CopyDir: %v\n", err) } + if err = git.InitOnceWithSync(context.Background()); err != nil { fatalTestError("git.Init: %v\n", err) } + git.CheckLFSVersion() ownerDirs, err := os.ReadDir(setting.RepoRootPath) if err != nil { @@ -202,7 +204,7 @@ func PrepareTestEnv(t testing.TB) { assert.NoError(t, util.RemoveAll(setting.RepoRootPath)) metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta") assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath)) - assert.NoError(t, git.InitOnceWithSync(context.Background())) + assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again ownerDirs, err := os.ReadDir(setting.RepoRootPath) assert.NoError(t, err) diff --git a/modules/git/commit.go b/modules/git/commit.go index 99fbbd0cfcb1e..82b5e0b25d082 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -206,26 +206,17 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) { return false, nil } - if err := CheckGitVersionAtLeast("1.8"); err == nil { - _, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path}) - if err == nil { - return true, nil + _, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path}) + if err == nil { + return true, nil + } + var exitError *exec.ExitError + if errors.As(err, &exitError) { + if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 { + return false, nil } - var exitError *exec.ExitError - if errors.As(err, &exitError) { - if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 { - return false, nil - } - } - return false, err - } - - result, _, err := NewCommand(c.repo.Ctx, "rev-list", "--ancestry-path", "-n1", that+".."+this, "--").RunStdString(&RunOpts{Dir: c.repo.Path}) - if err != nil { - return false, err } - - return len(strings.TrimSpace(result)) > 0, nil + return false, err } // CommitsBeforeLimit returns num commits before current revision diff --git a/modules/git/git.go b/modules/git/git.go index 0459f57dde124..d789a576ba13e 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -7,10 +7,10 @@ package git import ( "context" + "errors" "fmt" "os" "os/exec" - "path/filepath" "runtime" "strings" "sync" @@ -22,20 +22,16 @@ import ( "github.com/hashicorp/go-version" ) -var ( - // GitVersionRequired is the minimum Git version required - // At the moment, all code for git 1.x are not changed, if some users want to test with old git client - // or bypass the check, they still have a chance to edit this variable manually. - // If everything works fine, the code for git 1.x could be removed in a separate PR before 1.17 frozen. - GitVersionRequired = "2.0.0" +// GitVersionRequired is the minimum Git version required +const GitVersionRequired = "2.0.0" +var ( // GitExecutable is the command name of git // Could be updated to an absolute path while initialization GitExecutable = "git" - // DefaultContext is the default context to run git commands in - // will be overwritten by InitXxx with HammerContext - DefaultContext = context.Background() + // DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx + DefaultContext context.Context // SupportProcReceive version >= 2.29.0 SupportProcReceive bool @@ -128,36 +124,43 @@ func VersionInfo() string { return fmt.Sprintf(format, args...) } +func checkInit() error { + if setting.RepoRootPath == "" { + return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly") + } + if DefaultContext != nil { + log.Warn("git module has been initialized already, duplicate init should be fixed") + } + return nil +} + // HomeDir is the home dir for git to store the global config file used by Gitea internally func HomeDir() string { if setting.RepoRootPath == "" { - // TODO: now, some unit test code call the git module directly without initialization, which is incorrect. - // at the moment, we just use a temp HomeDir to prevent from conflicting with user's git config - // in the future, the git module should be initialized first before use. - tmpHomeDir := filepath.Join(os.TempDir(), "gitea-temp-home") - log.Error("Git's HomeDir is empty (RepoRootPath is empty), the git module is not initialized correctly, using a temp HomeDir (%s) temporarily", tmpHomeDir) - return tmpHomeDir + // strict check, make sure the git module is initialized correctly. + // attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users. + // for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons. + log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly") + return "" } return setting.RepoRootPath } // InitSimple initializes git module with a very simple step, no config changes, no global command arguments. // This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race +// However, in integration test, the sub-command function may be called in the current process, so the InitSimple would be called multiple times, too func InitSimple(ctx context.Context) error { + if err := checkInit(); err != nil { + return err + } + DefaultContext = ctx if setting.Git.Timeout.Default > 0 { defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second } - if err := SetExecutablePath(setting.Git.Path); err != nil { - return err - } - - // force cleanup args - globalCommandArgs = []string{} - - return nil + return SetExecutablePath(setting.Git.Path) } var initOnce sync.Once @@ -166,6 +169,10 @@ var initOnce sync.Once // This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too), // otherwise there will be data-race problem at the moment. func InitOnceWithSync(ctx context.Context) (err error) { + if err = checkInit(); err != nil { + return err + } + initOnce.Do(func() { err = InitSimple(ctx) if err != nil { diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 38818788f32a3..596a91e80382a 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -30,10 +30,10 @@ type CheckAttributeOpts struct { func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) { env := []string{} - if len(opts.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil { + if len(opts.IndexFile) > 0 { env = append(env, "GIT_INDEX_FILE="+opts.IndexFile) } - if len(opts.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil { + if len(opts.WorkTree) > 0 { env = append(env, "GIT_WORK_TREE="+opts.WorkTree) } @@ -56,8 +56,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[ } } - // git check-attr --cached first appears in git 1.7.8 - if opts.CachedOnly && CheckGitVersionAtLeast("1.7.8") == nil { + if opts.CachedOnly { cmdArgs = append(cmdArgs, "--cached") } @@ -125,12 +124,12 @@ type CheckAttributeReader struct { func (c *CheckAttributeReader) Init(ctx context.Context) error { cmdArgs := []string{"check-attr", "--stdin", "-z"} - if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil { + if len(c.IndexFile) > 0 { cmdArgs = append(cmdArgs, "--cached") c.env = append(c.env, "GIT_INDEX_FILE="+c.IndexFile) } - if len(c.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil { + if len(c.WorkTree) > 0 { c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree) } @@ -160,17 +159,10 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error { return err } - if CheckGitVersionAtLeast("1.8.5") == nil { - lw := new(nulSeparatedAttributeWriter) - lw.attributes = make(chan attributeTriple, 5) - lw.closed = make(chan struct{}) - c.stdOut = lw - } else { - lw := new(lineSeparatedAttributeWriter) - lw.attributes = make(chan attributeTriple, 5) - lw.closed = make(chan struct{}) - c.stdOut = lw - } + lw := new(nulSeparatedAttributeWriter) + lw.attributes = make(chan attributeTriple, 5) + lw.closed = make(chan struct{}) + c.stdOut = lw return nil } @@ -400,3 +392,37 @@ func (wr *lineSeparatedAttributeWriter) Close() error { close(wr.closed) return nil } + +// Create a check attribute reader for the current repository and provided commit ID +func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeReader, context.CancelFunc) { + indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID) + if err != nil { + return nil, func() {} + } + + checker := &CheckAttributeReader{ + Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, + Repo: repo, + IndexFile: indexFilename, + WorkTree: worktree, + } + ctx, cancel := context.WithCancel(repo.Ctx) + if err := checker.Init(ctx); err != nil { + log.Error("Unable to open checker for %s. Error: %v", commitID, err) + } else { + go func() { + err := checker.Run() + if err != nil && err != ctx.Err() { + log.Error("Unable to open checker for %s. Error: %v", commitID, err) + } + cancel() + }() + } + deferable := func() { + _ = checker.Close() + cancel() + deleteTemporaryFile() + } + + return checker, deferable +} diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 4b0cc8536b673..3c7af73000e45 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -255,13 +255,7 @@ func (repo *Repository) GetDiff(base, head string, w io.Writer) error { // GetDiffBinary generates and returns patch data between given revisions, including binary diffs. func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error { - if CheckGitVersionAtLeast("1.7.7") == nil { - return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{ - Dir: repo.Path, - Stdout: w, - }) - } - return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--patience", base, head).Run(&RunOpts{ + return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{ Dir: repo.Path, Stdout: w, }) diff --git a/modules/git/repo_language_stats_gogit.go b/modules/git/repo_language_stats_gogit.go index 3c9f026b7ace2..34b0dc45d3749 100644 --- a/modules/git/repo_language_stats_gogit.go +++ b/modules/git/repo_language_stats_gogit.go @@ -8,12 +8,10 @@ package git import ( "bytes" - "context" "io" "strings" "code.gitea.io/gitea/modules/analyze" - "code.gitea.io/gitea/modules/log" "github.com/go-enry/go-enry/v2" "github.com/go-git/go-git/v5" @@ -43,33 +41,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err return nil, err } - var checker *CheckAttributeReader - - if CheckGitVersionAtLeast("1.7.8") == nil { - indexFilename, workTree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID) - if err == nil { - defer deleteTemporaryFile() - checker = &CheckAttributeReader{ - Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, - Repo: repo, - IndexFile: indexFilename, - WorkTree: workTree, - } - ctx, cancel := context.WithCancel(DefaultContext) - if err := checker.Init(ctx); err != nil { - log.Error("Unable to open checker for %s. Error: %v", commitID, err) - } else { - go func() { - err = checker.Run() - if err != nil { - log.Error("Unable to open checker for %s. Error: %v", commitID, err) - cancel() - } - }() - } - defer cancel() - } - } + checker, deferable := repo.CheckAttributeReader(commitID) + defer deferable() sizes := make(map[string]int64) err = tree.Files().ForEach(func(f *object.File) error { diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go index 41b176f816912..d237924f92a4c 100644 --- a/modules/git/repo_language_stats_nogogit.go +++ b/modules/git/repo_language_stats_nogogit.go @@ -9,7 +9,6 @@ package git import ( "bufio" "bytes" - "context" "io" "math" "strings" @@ -63,36 +62,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err return nil, err } - var checker *CheckAttributeReader - - if CheckGitVersionAtLeast("1.7.8") == nil { - indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID) - if err == nil { - defer deleteTemporaryFile() - checker = &CheckAttributeReader{ - Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, - Repo: repo, - IndexFile: indexFilename, - WorkTree: worktree, - } - ctx, cancel := context.WithCancel(repo.Ctx) - if err := checker.Init(ctx); err != nil { - log.Error("Unable to open checker for %s. Error: %v", commitID, err) - } else { - go func() { - err = checker.Run() - if err != nil { - log.Error("Unable to open checker for %s. Error: %v", commitID, err) - cancel() - } - }() - } - defer func() { - _ = checker.Close() - cancel() - }() - } - } + checker, deferable := repo.CheckAttributeReader(commitID) + defer deferable() contentBuf := bytes.Buffer{} var content []byte diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go index 2e139daddfc72..2ea3f0187a185 100644 --- a/modules/git/repo_tree.go +++ b/modules/git/repo_tree.go @@ -45,11 +45,11 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt _, _ = messageBytes.WriteString(opts.Message) _, _ = messageBytes.WriteString("\n") - if CheckGitVersionAtLeast("1.7.9") == nil && (opts.KeyID != "" || opts.AlwaysSign) { + if opts.KeyID != "" || opts.AlwaysSign { cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID)) } - if CheckGitVersionAtLeast("2.0.0") == nil && opts.NoGPGSign { + if opts.NoGPGSign { cmd.AddArguments("--no-gpg-sign") } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index f494998c59663..a7cf81250bf0e 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -5,12 +5,14 @@ package markup_test import ( + "context" "io" "strings" "testing" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" . "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" @@ -25,6 +27,13 @@ var localMetas = map[string]string{ "repoPath": "../../integrations/gitea-repositories-meta/user13/repo11.git/", } +func TestMain(m *testing.M) { + setting.LoadAllowEmpty() + if err := git.InitSimple(context.Background()); err != nil { + log.Fatal("git init failed, err: %v", err) + } +} + func TestRender_Commits(t *testing.T) { setting.AppURL = TestAppURL test := func(input, expected string) { diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index a069d402bb506..732fe1a6beff9 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -5,6 +5,7 @@ package markdown_test import ( + "context" "strings" "testing" @@ -31,6 +32,13 @@ var localMetas = map[string]string{ "repoPath": "../../../integrations/gitea-repositories-meta/user13/repo11.git/", } +func TestMain(m *testing.M) { + setting.LoadAllowEmpty() + if err := git.InitSimple(context.Background()); err != nil { + log.Fatal("git init failed, err: %v", err) + } +} + func TestRender_StandardLinks(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL diff --git a/modules/repository/init.go b/modules/repository/init.go index f5cef3301d5e9..e984697cda962 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -323,19 +323,17 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi "-m", "Initial commit", } - if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u) - if sign { - args = append(args, "-S"+keyID) - - if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { - // need to set the committer to the KeyID owner - committerName = signer.Name - committerEmail = signer.Email - } - } else if git.CheckGitVersionAtLeast("2.0.0") == nil { - args = append(args, "--no-gpg-sign") + sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u) + if sign { + args = append(args, "-S"+keyID) + + if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { + // need to set the committer to the KeyID owner + committerName = signer.Name + committerEmail = signer.Email } + } else { + args = append(args, "--no-gpg-sign") } env = append(env, diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 97daadbc67331..37dc0e114dac7 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1417,37 +1417,8 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff } diff.Start = opts.SkipTo - var checker *git.CheckAttributeReader - - if git.CheckGitVersionAtLeast("1.7.8") == nil { - indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(opts.AfterCommitID) - if err == nil { - defer deleteTemporaryFile() - - checker = &git.CheckAttributeReader{ - Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, - Repo: gitRepo, - IndexFile: indexFilename, - WorkTree: worktree, - } - ctx, cancel := context.WithCancel(gitRepo.Ctx) - if err := checker.Init(ctx); err != nil { - log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err) - } else { - go func() { - err := checker.Run() - if err != nil && err != ctx.Err() { - log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err) - } - cancel() - }() - } - defer func() { - _ = checker.Close() - cancel() - }() - } - } + checker, deferable := gitRepo.CheckAttributeReader(opts.AfterCommitID) + defer deferable() for _, diffFile := range diff.Files { diff --git a/services/pull/merge.go b/services/pull/merge.go index aff800a1b65bb..e8bb3a1cdd23d 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -276,15 +276,8 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode return "", fmt.Errorf("Unable to write .git/info/sparse-checkout file in tmpBasePath: %v", err) } - var gitConfigCommand func() *git.Command - if git.CheckGitVersionAtLeast("1.8.0") == nil { - gitConfigCommand = func() *git.Command { - return git.NewCommand(ctx, "config", "--local") - } - } else { - gitConfigCommand = func() *git.Command { - return git.NewCommand(ctx, "config") - } + gitConfigCommand := func() *git.Command { + return git.NewCommand(ctx, "config", "--local") } // Switch off LFS process (set required, clean and smudge here also) @@ -366,16 +359,14 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode // Determine if we should sign signArg := "" - if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch) - if sign { - signArg = "-S" + keyID - if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { - committer = signer - } - } else if git.CheckGitVersionAtLeast("2.0.0") == nil { - signArg = "--no-gpg-sign" + sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch) + if sign { + signArg = "-S" + keyID + if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { + committer = signer } + } else { + signArg = "--no-gpg-sign" } commitTimeStr := time.Now().Format(time.RFC3339) diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 97a80a96bd6ab..1e60d55613fa0 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -248,34 +248,31 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co args = []string{"commit-tree", treeHash} } - // Determine if we should sign - if git.CheckGitVersionAtLeast("1.7.9") == nil { - var sign bool - var keyID string - var signer *git.Signature - if parent != "" { - sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent) - } else { - sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author) - } - if sign { - args = append(args, "-S"+keyID) - if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { - if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email { - // Add trailers - _, _ = messageBytes.WriteString("\n") - _, _ = messageBytes.WriteString("Co-authored-by: ") - _, _ = messageBytes.WriteString(committerSig.String()) - _, _ = messageBytes.WriteString("\n") - _, _ = messageBytes.WriteString("Co-committed-by: ") - _, _ = messageBytes.WriteString(committerSig.String()) - _, _ = messageBytes.WriteString("\n") - } - committerSig = signer + var sign bool + var keyID string + var signer *git.Signature + if parent != "" { + sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent) + } else { + sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author) + } + if sign { + args = append(args, "-S"+keyID) + if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { + if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email { + // Add trailers + _, _ = messageBytes.WriteString("\n") + _, _ = messageBytes.WriteString("Co-authored-by: ") + _, _ = messageBytes.WriteString(committerSig.String()) + _, _ = messageBytes.WriteString("\n") + _, _ = messageBytes.WriteString("Co-committed-by: ") + _, _ = messageBytes.WriteString(committerSig.String()) + _, _ = messageBytes.WriteString("\n") } - } else if git.CheckGitVersionAtLeast("2.0.0") == nil { - args = append(args, "--no-gpg-sign") + committerSig = signer } + } else { + args = append(args, "--no-gpg-sign") } if signoff { From 89b0aac37449cf7ccdfa52c6edbe537257228bc1 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 16 Jun 2022 16:10:29 +0000 Subject: [PATCH 07/35] [skip ci] Updated translations via Crowdin --- options/locale/locale_zh-TW.ini | 159 +++++++++++++++++--------------- 1 file changed, 86 insertions(+), 73 deletions(-) diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index efeb4cb99cb1f..c65885a1e7e34 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -131,7 +131,7 @@ license_desc=取得 安裝指南再來調整設定。 -require_db_desc=Gitea 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB(MySQL 協定)等其中一項。 +require_db_desc=Gitea 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。 db_title=資料庫設定 db_type=資料庫類型 host=主機 @@ -213,7 +213,7 @@ confirm_password=確認密碼 admin_email=電子信箱 install_btn_confirm=安裝 Gitea test_git_failed=無法識別「git」命令:%v -sqlite3_not_available=您目前的版本不支援 SQLite3,請從 %s 下載官方的預先編譯版本(不是 gobuild 版本)。 +sqlite3_not_available=您目前的版本不支援 SQLite3,請從 %s 下載官方的預先編譯版本 (不是 gobuild 版本)。 invalid_db_setting=資料庫設定不正確: %v invalid_db_table=資料庫的資料表「%s」無效:%v invalid_repo_path=儲存庫根目錄設定不正確:%v @@ -391,13 +391,13 @@ issue.action.ready_for_review=@%[1]s 標記了此合併請求為準備好 issue.action.new=@%[1]s 建立了 #%[2]d。 issue.in_tree_path=在 %s 中: -release.new.subject=%[2]s 中的 %[1]s 發佈了 -release.new.text=@%[1]s 於 %[3]s 發佈了 %[2]s +release.new.subject=%[2]s 中的 %[1]s 發布了 +release.new.text=@%[1]s 於 %[3]s 發布了 %[2]s release.title=標題:%s release.note=說明: release.downloads=下載: -release.download.zip=原始碼(ZIP) -release.download.targz=原始碼(TAR.GZ) +release.download.zip=原始碼 (ZIP) +release.download.targz=原始碼 (TAR.GZ) repo.transfer.subject_to=%s 想要把「%s」轉移給 %s repo.transfer.subject_to_you=%s 想要把「%s」轉移給您 @@ -770,7 +770,7 @@ or_enter_secret=或者輸入密碼: %s then_enter_passcode=然後輸入應用程式中顯示的驗證碼: passcode_invalid=無效的驗證碼,請重試。 twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到一個安全的地方,它只會顯示這麼一次! -twofa_failed_get_secret=取得密鑰(Secret)失敗。 +twofa_failed_get_secret=取得密鑰 (Secret) 失敗。 webauthn_desc=安全金鑰是包含加密密鑰的硬體設備,它們可以用於兩步驟驗證。安全金鑰必須支援 WebAuthn Authenticator 標準。 webauthn_register_key=新增安全金鑰 @@ -839,7 +839,7 @@ download_bundle=下載 BUNDLE generate_repo=產生儲存庫 generate_from=產生自 repo_desc=儲存庫描述 -repo_desc_helper=輸入簡介(非必要) +repo_desc_helper=輸入簡介 (選用) repo_lang=儲存庫語言 repo_gitignore_helper=選擇 .gitignore 範本 repo_gitignore_helper_desc=從常見語言範本清單中挑選忽略追蹤的檔案。預設情況下各種語言建置工具產生的特殊檔案都包含在 .gitignore 中。 @@ -851,12 +851,12 @@ license_helper_desc=授權條款定義了他人使用您原始碼的允許和禁 readme=讀我檔案 readme_helper=選擇讀我檔案範本。 readme_helper_desc=這是您能為專案撰寫完整描述的地方。 -auto_init=初始化儲存庫(建立 .gitignore、授權條款和讀我檔案) -trust_model_helper=選擇簽署驗證的信任模型。可用的選項: -trust_model_helper_collaborator=協作者:信任協作者的簽署 -trust_model_helper_committer=提交者:信任與提交者相符的簽署 -trust_model_helper_collaborator_committer=協作者 + 提交者:信任協作者同時是提交者的簽署 -trust_model_helper_default=預設:使用此 Gitea 的預設儲存庫信任模式 +auto_init=初始化儲存庫 (加入 .gitignore、授權條款和讀我檔案) +trust_model_helper=選擇簽署驗證的信任模型。可用的選項: +trust_model_helper_collaborator=協作者: 信任協作者的簽署 +trust_model_helper_committer=提交者: 信任與提交者相符的簽署 +trust_model_helper_collaborator_committer=協作者 + 提交者: 信任協作者同時是提交者的簽署 +trust_model_helper_default=預設: 使用此 Gitea 的預設儲存庫信任模式 create_repo=建立儲存庫 default_branch=預設分支 default_branch_helper=預設分支是合併請求和提交程式碼的基礎分支。 @@ -945,7 +945,7 @@ migrate_items_labels=標籤 migrate_items_issues=問題 migrate_items_pullrequests=合併請求 migrate_items_merge_requests=合併請求 -migrate_items_releases=版本發佈 +migrate_items_releases=版本發布 migrate_repo=遷移儲存庫 migrate.clone_address=從 URL 遷移 / Clone migrate.clone_address_desc=現有存儲庫的 HTTP(S) 或 Git Clone URL @@ -976,7 +976,7 @@ migrate.migrating_git=正在遷移 Git 資料 migrate.migrating_topics=正在遷移主題 migrate.migrating_milestones=正在遷移里程碑 migrate.migrating_labels=正在遷移標籤 -migrate.migrating_releases=正在遷移版本發佈 +migrate.migrating_releases=正在遷移版本發布 migrate.migrating_issues=正在遷移問題 migrate.migrating_pulls=正在遷移合併請求 @@ -1022,10 +1022,10 @@ org_labels_desc_manage=管理 milestones=里程碑 commits=提交歷史 commit=提交 -release=版本發佈 -releases=版本發佈 +release=版本發布 +releases=版本發布 tag=標籤 -released_this=發佈了此版本 +released_this=發布了此版本 file.title=%s 於 %s file_raw=原始文件 file_history=歷史記錄 @@ -1036,10 +1036,10 @@ file_permalink=永久連結 file_too_large=檔案太大,無法顯示。 bidi_bad_header=`此檔案含有未預期的 Bidirectional Unicode 字元!` bidi_bad_description=`此檔案含有未預期的 Bidirectional Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。` -bidi_bad_description_escaped=`此檔案含有未預期的 Bidirectional Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫(Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。` +bidi_bad_description_escaped=`此檔案含有未預期的 Bidirectional Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫 (Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。` unicode_header=`此檔案含有隱藏的 Unicode 字元!` unicode_description=`此檔案含有隱藏的 Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。` -unicode_description_escaped=`此檔案含有隱藏的 Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫(Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。` +unicode_description_escaped=`此檔案含有隱藏的 Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫 (Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。` line_unicode=`這一行有隱藏的 Unicode 字元` escape_control_characters=Escape @@ -1088,7 +1088,7 @@ editor.patch=套用 Patch editor.patching=正在 Patch: editor.fail_to_apply_patch=無法套用 Patch「%s」 editor.new_patch=新增 Patch -editor.commit_message_desc=(選填)加入詳細說明... +editor.commit_message_desc=(選用) 加入詳細說明... editor.signoff_desc=在提交訊息底部加入提交者的「Signed-off-by」資訊。 editor.commit_directly_to_this_branch=直接提交到 %s 分支。 editor.create_new_branch=為此提交建立新分支並提出合併請求。 @@ -1158,7 +1158,7 @@ ext_issues.desc=連結到外部問題追蹤器。 projects=專案 projects.desc=在專案看板中管理問題與合併請求。 -projects.description=描述(非必要) +projects.description=描述 (選用) projects.description_placeholder=描述 projects.create=建立專案 projects.title=標題 @@ -1329,7 +1329,7 @@ issues.ref_reopening_from=`關聯了合併請求 %[4]s 將重新 issues.ref_closed_from=`關閉了這個問題 %[4]s %[2]s` issues.ref_reopened_from=`重新開放了這個問題 %[4]s %[2]s` issues.ref_from=`自 %[1]s` -issues.poster=發佈者 +issues.poster=發布者 issues.collaborator=協作者 issues.owner=擁有者 issues.re_request_review=再次請求審核 @@ -1383,7 +1383,7 @@ issues.unlock.title=解鎖此問題的對話。 issues.comment_on_locked=您無法在已鎖定的問題上留言。 issues.delete=刪除 issues.delete.title=刪除此問題? -issues.delete.text=您真的要刪除此問題嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。) +issues.delete.text=您真的要刪除此問題嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。) issues.tracker=時間追蹤 issues.start_tracking_short=開始計時 issues.start_tracking=開始時間追蹤 @@ -1441,8 +1441,8 @@ issues.dependency.pr_close_blocked=在您合併以前,您必須先關閉所有 issues.dependency.blocks_short=阻擋 issues.dependency.blocked_by_short=先決於 issues.dependency.remove_header=移除先決條件 -issues.dependency.issue_remove_text=即將從此問題移除先決條件。是否繼續? -issues.dependency.pr_remove_text=即將從此合併請求移除先決條件。是否繼續? +issues.dependency.issue_remove_text=即將從此問題移除先決條件。是否繼續? +issues.dependency.pr_remove_text=即將從此合併請求移除先決條件。是否繼續? issues.dependency.setting=啟用問題及合併請求的先決條件 issues.dependency.add_error_same_issue=您無法將問題設定為自己的先決條件。 issues.dependency.add_error_dep_issue_not_exist=先決條件問題不存在。 @@ -1524,7 +1524,7 @@ pulls.manually_merged_as=此合併請求已被手動合併為 標題用 %s 開頭以避免意外地合併此合併請求。` -pulls.cannot_merge_work_in_progress=此合併請求被標記為還在進行中(WIP)。 +pulls.cannot_merge_work_in_progress=此合併請求被標記為還在進行中 (WIP)。 pulls.still_in_progress=還在進行中嗎? pulls.add_prefix=加入 %s 前綴 pulls.remove_prefix=移除 %s 前綴 @@ -1621,7 +1621,7 @@ milestones.completeness=%d%% 完成 milestones.create=建立里程碑 milestones.title=標題 milestones.desc=描述 -milestones.due_date=截止日期(可選) +milestones.due_date=截止日期 (選用) milestones.clear=清除 milestones.invalid_due_date_format=截止日期的格式必須為「yyyy-mm-dd」。 milestones.create_success=已建立里程碑「%s」。 @@ -1723,8 +1723,8 @@ activity.unresolved_conv_desc=這些最近更改的問題和合併請求尚未 activity.unresolved_conv_label=開放 activity.title.releases_1=%d 個版本 activity.title.releases_n=%d 個版本 -activity.title.releases_published_by=%[2]s發佈了 %[1]s -activity.published_release_label=已發佈 +activity.title.releases_published_by=%[2]s發布了 %[1]s +activity.published_release_label=已發布 activity.no_git_activity=期間內沒有任何提交動態 activity.git_stats_exclude_merges=不計合併, activity.git_stats_author_1=%d 位作者 @@ -1817,7 +1817,7 @@ settings.pulls.allow_rebase_merge=啟用 Rebase 合併提交 settings.pulls.allow_rebase_merge_commit=啟用 Rebase 顯式合併提交(--no-ff) settings.pulls.allow_squash_commits=啟用 Squash 合併提交 settings.pulls.allow_manual_merge=允許將合併請求標記為手動合併 -settings.pulls.enable_autodetect_manual_merge=啟用自動偵測手動合併(注意:在某些特殊情況下可能發生誤判) +settings.pulls.enable_autodetect_manual_merge=啟用自動偵測手動合併 (注意: 在某些特殊情況下可能發生誤判) settings.pulls.allow_rebase_update=啟用透過 Rebase 更新合併請求分支 settings.pulls.default_delete_branch_after_merge=預設在合併後刪除合併請求分支 settings.packages_desc=啟用儲存庫套件註冊中心 @@ -1854,7 +1854,7 @@ settings.transfer_form_title=輸入儲存庫名稱以確認: settings.transfer_in_progress=目前正在進行轉移。如果您想要將此儲存庫轉移給其他使用者,請取消他。 settings.transfer_notices_1=- 如果將此儲存庫轉移給個別使用者,您將會失去此儲存庫的存取權。 settings.transfer_notices_2=- 如果將此儲存庫轉移到您(共同)擁有的組織,您將能繼續保有此儲存庫的存取權。 -settings.transfer_notices_3=- 如果此儲存庫為私有儲存庫且將轉移給個別使用者,此動作確保該使用者至少擁有讀取權限(必要時將會修改權限)。 +settings.transfer_notices_3=- 如果此儲存庫為私有儲存庫且將轉移給個別使用者,此動作確保該使用者至少擁有讀取權限 (必要時將會修改權限)。 settings.transfer_owner=新擁有者 settings.transfer_perform=進行轉移 settings.transfer_started=此儲存庫已被標記為待轉移且正在等待「%s」的確認 @@ -1864,13 +1864,13 @@ settings.trust_model=簽署信任模式 settings.trust_model.default=預設信任模式 settings.trust_model.default.desc=使用此 Gitea 的預設儲存庫信任模式。 settings.trust_model.collaborator=協作者 -settings.trust_model.collaborator.long=協作者:信任協作者的簽署 -settings.trust_model.collaborator.desc=此儲存庫協作者的有效簽署將被標記為「受信任」(無論它們是否符合提交者),簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。 +settings.trust_model.collaborator.long=協作者: 信任協作者的簽署 +settings.trust_model.collaborator.desc=此儲存庫協作者的有效簽署將被標記為「受信任」(無論它們是否符合提交者),簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。 settings.trust_model.committer=提交者 -settings.trust_model.committer.long=提交者:信任與提交者相符的簽署(此選項與 GitHub 相同,這會強制 Gitea 簽署提交並以 Gitea 作為提交者) +settings.trust_model.committer.long=提交者: 信任與提交者相符的簽署 (此選項與 GitHub 相同,這會強制 Gitea 簽署提交並以 Gitea 作為提交者) settings.trust_model.committer.desc=提交者的有效簽署將被標記為「受信任」,否則將被標記為「不符合」。這會強制 Gitea 成為受簽署提交的提交者,實際的提交者將於提交訊息結尾被標記為「Co-authored-by:」和「Co-committed-by:」。預設的 Gitea 金鑰必須符合資料庫中的一位使用者。 settings.trust_model.collaboratorcommitter=協作者+提交者 -settings.trust_model.collaboratorcommitter.long=協作者 + 提交者:信任協作者同時是提交者的簽署 +settings.trust_model.collaboratorcommitter.long=協作者 + 提交者: 信任協作者同時是提交者的簽署 settings.trust_model.collaboratorcommitter.desc=此儲存庫協作者的有效簽署在他同時是提交者時將被標記為「受信任」,簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。這會強制 Gitea 成為受簽署提交的提交者,實際的提交者將於提交訊息結尾被標記為「Co-Authored-By:」和「Co-Committed-By:」。預設的 Gitea 金鑰必須符合資料庫中的一位使用者。 settings.wiki_delete=刪除 Wiki 資料 settings.wiki_delete_desc=刪除儲存庫 Wiki 資料是永久的且不可還原。 @@ -1946,8 +1946,8 @@ settings.event_delete=刪除 settings.event_delete_desc=刪除分支或標籤。 settings.event_fork=Fork settings.event_fork_desc=儲存庫已被 fork。 -settings.event_release=版本發佈 -settings.event_release_desc=在儲存庫中發佈、更新或刪除版本。 +settings.event_release=版本發布 +settings.event_release_desc=在儲存庫中發布、更新或刪除版本。 settings.event_push=推送 settings.event_push_desc=推送到儲存庫。 settings.event_repository=儲存庫 @@ -2060,9 +2060,9 @@ settings.dismiss_stale_approvals=捨棄過時的核可 settings.dismiss_stale_approvals_desc=當新的提交有修改到合併請求的內容,並被推送到此分支時,將捨棄舊的核可。 settings.require_signed_commits=僅接受經簽署的提交 settings.require_signed_commits_desc=拒絕未經簽署或未經驗證的提交推送到此分支。 -settings.protect_protected_file_patterns=受保護的檔案模式(以分號區隔「\;」): +settings.protect_protected_file_patterns=受保護的檔案模式 (以分號區隔「\;」): settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「\;」分隔多個模式。請於github.com/gobwas/glob 文件查看模式格式。範例:.drone.yml, /docs/**/*.txt。 -settings.protect_unprotected_file_patterns=未受保護的檔案模式(以分號區隔「\;」): +settings.protect_unprotected_file_patterns=未受保護的檔案模式 (以分號區隔「\;」): settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「\;」分隔多個模式。請於github.com/gobwas/glob 文件查看模式格式。範例:.drone.yml, /docs/**/*.txt。 settings.add_protected_branch=啟用保護 settings.delete_protected_branch=停用保護 @@ -2197,20 +2197,20 @@ diff.image.overlay=重疊 diff.has_escaped=這一行有隱藏的 Unicode 字元 releases.desc=追蹤專案版本和檔案下載。 -release.releases=版本發佈 +release.releases=版本發布 release.detail=版本詳情 release.tags=標籤 -release.new_release=發佈新版本 +release.new_release=發布新版本 release.draft=草稿 -release.prerelease=預發佈版本 +release.prerelease=預發布版本 release.stable=穩定 release.compare=比較 release.edit=編輯 release.ahead.commits=%d 次提交 -release.ahead.target=在此版本發佈後被加入到 %s +release.ahead.target=在此版本發布後被加入到 %s release.source_code=程式碼 -release.new_subheader=發佈、整理專案的版本。 -release.edit_subheader=發佈、整理專案的版本。 +release.new_subheader=發布、整理專案的版本。 +release.edit_subheader=發布、整理專案的版本。 release.tag_name=標籤名稱 release.target=目標分支 release.tag_helper=新增或選擇一個既有的標籤。 @@ -2219,17 +2219,17 @@ release.content=內容 release.prerelease_desc=標記為 Pre-Release release.prerelease_helper=標記此版本不適合生產使用。 release.cancel=取消 -release.publish=發佈版本 +release.publish=發布版本 release.save_draft=儲存草稿 -release.edit_release=更新發佈 -release.delete_release=刪除發佈 +release.edit_release=更新發布 +release.delete_release=刪除發布 release.delete_tag=刪除標籤 -release.deletion=刪除發佈 -release.deletion_desc=刪除版本發佈只會將它從 Gitea 移除。儲存庫內容和歷史將保持不變,是否繼續? -release.deletion_success=已刪除此版本發佈。 +release.deletion=刪除發布 +release.deletion_desc=刪除版本發布只會將它從 Gitea 移除。儲存庫內容和歷史將保持不變,是否繼續? +release.deletion_success=已刪除此版本發布。 release.deletion_tag_desc=即將從儲存庫移除此標籤。儲存庫內容和歷史將保持不變,是否繼續? release.deletion_tag_success=已刪除此標籤。 -release.tag_name_already_exist=已經存在使用相同標籤的發佈版本。 +release.tag_name_already_exist=已經存在使用相同標籤的發布版本。 release.tag_name_invalid=標籤名稱無效。 release.tag_name_protected=標籤名稱已受保護。 release.tag_already_exist=此標籤名稱已存在。 @@ -2324,9 +2324,9 @@ settings.permission=權限 settings.repoadminchangeteam=儲存庫管理者可增加與移除團隊權限 settings.visibility=瀏覽權限 settings.visibility.public=公開 -settings.visibility.limited=受限(只有登入的使用者才能看到) +settings.visibility.limited=受限 (只有登入的使用者才能看到) settings.visibility.limited_shortname=受限 -settings.visibility.private=私有(只有組織成員才能看到) +settings.visibility.private=私有 (只有組織成員才能看到) settings.visibility.private_shortname=私有 settings.update_settings=更新設定 @@ -2425,7 +2425,7 @@ dashboard.new_version_hint=現已推出 Gitea %s,您正在執行 %s。查看%d 位使用者,%d 個組織,%d 個公鑰,%d 個儲存庫,%d 個儲存庫關注,%d 個星號,~%d 次行為,%d 條權限記錄,%d 個問題,%d 則留言,%d 個社群帳戶,%d 個使用者關注,%d 個鏡像,%d 個版本發佈,%d 個認證來源,%d 個 Webhook ,%d 個里程碑,%d 個標籤,%d 個 Hook 任務,%d 個團隊,%d 個更新任務,%d 個附件。 +dashboard.statistic_info=Gitea 資料庫統計: %d 位使用者,%d 個組織,%d 個公鑰,%d 個儲存庫,%d 個儲存庫關注,%d 個星號,~%d 次行為,%d 條權限記錄,%d 個問題,%d 則留言,%d 個社群帳戶,%d 個使用者追蹤,%d 個鏡像,%d 個版本發布,%d 個認證來源,%d 個 Webhook ,%d 個里程碑,%d 個標籤,%d 個 Hook 任務,%d 個團隊,%d 個更新任務,%d 個附件。 dashboard.operation_name=作業名稱 dashboard.operation_switch=開關 dashboard.operation_run=執行 @@ -2454,7 +2454,7 @@ dashboard.repo_health_check=對所有儲存庫進行健康檢查 dashboard.check_repo_stats=檢查所有儲存庫的統計資料 dashboard.archive_cleanup=刪除舊的儲存庫存檔 dashboard.deleted_branches_cleanup=清理已刪除的分支 -dashboard.update_migration_poster_id=更新遷移發佈者 ID +dashboard.update_migration_poster_id=更新遷移發布者 ID dashboard.git_gc_repos=對所有儲存庫進行垃圾回收 dashboard.resync_all_sshkeys=使用 Gitea 的 SSH 金鑰更新「.ssh/authorized_keys」檔案。 dashboard.resync_all_sshkeys.desc=(內建 SSH 伺服器無需使用。) @@ -2562,7 +2562,7 @@ emails.updated=信箱已更新 emails.not_updated=電子信箱更新失敗: %v emails.duplicate_active=此信箱已被其他使用者使用 emails.change_email_header=更新電子信箱屬性 -emails.change_email_text=您確定要更新這個電子信箱? +emails.change_email_text=您確定要更新這個電子信箱? orgs.org_manage_panel=組織管理 orgs.name=組織名稱 @@ -2635,11 +2635,11 @@ auths.filter=使用者篩選器 auths.admin_filter=管理者篩選器 auths.restricted_filter=受限制的篩選器 auths.restricted_filter_helper=留白則不限制任何使用者。使用米字「*」將所有不符合管理員篩選條件的使用者設定為受限。 -auths.verify_group_membership=驗證 LDAP 群組成員資格(篩選器留空以跳過) +auths.verify_group_membership=驗證 LDAP 群組成員資格 (篩選器留空以跳過) auths.group_search_base=群組搜尋的 Base DN auths.group_attribute_list_users=包含使用者清單的群組屬性 auths.user_attribute_in_group=群組中列出的使用者屬性 -auths.map_group_to_team=對應 LDAP 群組到組織團隊(欄位留空以跳過) +auths.map_group_to_team=對應 LDAP 群組到組織團隊 (欄位留空以跳過) auths.map_group_to_team_removal=如果使用者不屬於相對應的 LDAP 群組,將使用者從已同步的團隊移除。 auths.enable_ldap_groups=啟用 LDAP 群組 auths.ms_ad_sa=MS AD 搜尋屬性 @@ -2650,12 +2650,12 @@ auths.allowed_domains=域名白名單 auths.allowed_domains_helper=留白以允許所有域名。以半形逗號「,」分隔多個域名。 auths.skip_tls_verify=忽略 TLS 驗證 auths.force_smtps=強制 SMTPS -auths.force_smtps_helper=SMTPS 總是使用 465 埠。設定此選項以強制 SMTPS 使用其他埠。(除此之外若主機支援的話 STARTTLS 也會使用該埠。) +auths.force_smtps_helper=SMTPS 總是使用 465 埠。設定此選項以強制 SMTPS 使用其他埠。(除此之外若主機支援的話 STARTTLS 也會使用該埠。) auths.helo_hostname=HELO 主機名稱 auths.helo_hostname_helper=用 HELO 傳送的主機名稱。留空以傳送目前的主機名稱。 auths.disable_helo=停用 HELO auths.pam_service_name=PAM 服務名稱 -auths.pam_email_domain=PAM 電子信箱域名(非必要) +auths.pam_email_domain=PAM 電子信箱域名 (選用) auths.oauth2_provider=OAuth2 提供者 auths.oauth2_icon_url=圖示 URL auths.oauth2_clientID=客戶端 ID (金鑰) @@ -2669,23 +2669,23 @@ auths.oauth2_emailURL=電子郵件 URL auths.skip_local_two_fa=跳過本地兩步驟驗證 auths.skip_local_two_fa_helper=保持未設定代表使用兩步驟驗證的本地使用者仍然需要通過兩步驟驗證才能登入 auths.oauth2_tenant=租戶 -auths.oauth2_scopes=額外的授權範圍(Scope) +auths.oauth2_scopes=額外的授權範圍 (Scope) auths.oauth2_required_claim_name=必須填寫 Claim 名稱 auths.oauth2_required_claim_name_helper=填寫此名稱以限制 Claim 中有此名稱的使用者才能從此來源登入 auths.oauth2_required_claim_value=必須填寫 Claim 值 auths.oauth2_required_claim_value_helper=填寫此名稱以限制 Claim 中有此名稱和值的使用者才能從此來源登入 -auths.oauth2_group_claim_name=Claim 名稱提供群組名稱給此來源。(選用) -auths.oauth2_admin_group=管理員使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱) -auths.oauth2_restricted_group=受限制使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱) +auths.oauth2_group_claim_name=Claim 名稱提供群組名稱給此來源。(選用) +auths.oauth2_admin_group=管理員使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱) +auths.oauth2_restricted_group=受限制使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱) auths.enable_auto_register=允許授權用戶自動註冊 auths.sspi_auto_create_users=自動建立使用者 auths.sspi_auto_create_users_helper=允許 SSPI 認證方法於使用者首次登入時自動建立新帳戶 auths.sspi_auto_activate_users=自動啟用使用者 auths.sspi_auto_activate_users_helper=允許 SSPI 認證方法自動啟用新使用者 auths.sspi_strip_domain_names=從帳號中移除域名 -auths.sspi_strip_domain_names_helper=勾選後,將從登入名稱中移除域名(例如:「DOMAIN\user」和「user@example.org」都會變成「user」) +auths.sspi_strip_domain_names_helper=勾選後,將從登入名稱中移除域名 (例如:「DOMAIN\user」和「user@example.org」都會變成「user」) auths.sspi_separator_replacement=用來替換 \, / 和 @ 的分隔符號 -auths.sspi_separator_replacement_helper=用來替換下級登入名稱分隔符號的字元(例如:「DOMAIN\user」中的「\」)和使用者主體名稱(例如:「user@example.org」中的「@」)。 +auths.sspi_separator_replacement_helper=用來替換下級登入名稱分隔符號的字元 (例如:「DOMAIN\user」中的「\」) 和使用者主體名稱 (例如:「user@example.org」中的「@」)。 auths.sspi_default_language=使用者預設語言 auths.sspi_default_language_helper=SSPI 認證方法自動建立之使用者的預設語言,留白以自動偵測。 auths.tips=幫助提示 @@ -2704,7 +2704,7 @@ auths.tip.twitter=建立應用程式並確保有啟用「Allow this application auths.tip.discord=註冊新的應用程式。網址:https://discordapp.com/developers/applications/me auths.tip.gitea=註冊新的 OAuth2 應用程式。到 https://docs.gitea.io/en-us/oauth2-provider/ 觀看指南 auths.tip.yandex=建立新的應用程式,從「Yandex.Passport API」區塊選擇「Access to email address」、「Access to user avatar」和「Access to username, first name and surname, gender」。網址:https://oauth.yandex.com/client/new -auths.tip.mastodon=輸入您欲認證的 Mastodon 執行個體的自訂網址(或使用預設值) +auths.tip.mastodon=輸入您欲認證的 Mastodon 執行個體的自訂網址 (或使用預設值) auths.edit=修改認證來源 auths.activated=該認證來源已啟用 auths.new_success=已增加認證「%s」。 @@ -2831,8 +2831,8 @@ config.enable_federated_avatar=啟用 Federated Avatars config.git_config=Git 組態 config.git_disable_diff_highlight=停用比較語法高亮 -config.git_max_diff_lines=差異比較時顯示的最多行數(單檔) -config.git_max_diff_line_characters=差異比較時顯示的最多字元數(單行) +config.git_max_diff_lines=差異比較時顯示的最多行數 (單檔) +config.git_max_diff_line_characters=差異比較時顯示的最多字元數 (單行) config.git_max_diff_files=差異比較時顯示的最多檔案數 config.git_gc_args=GC 參數 config.git_migrate_timeout=遷移逾時 @@ -2970,7 +2970,7 @@ mirror_sync_create=從鏡像同步了新參考 %[3]s%[3]s 刪除了參考 %[2]s approve_pull_request=`核可了 %[3]s#%[2]s` reject_pull_request=`提出了修改建議 %[3]s#%[2]s` -publish_release=`發佈了 %[3]s "%[4]s" ` +publish_release=`發布了 %[3]s "%[4]s" ` review_dismissed=`取消了 %[4]s%[3]s#%[2]s 的審核` review_dismissed_reason=原因: create_branch=在 %[4]s 中建立了分支 %[3]s @@ -3036,6 +3036,7 @@ error.unit_not_allowed=您未被允許訪問此儲存庫區域 title=套件 desc=管理儲存庫套件。 empty=目前還沒有套件。 +empty.documentation=關於套件註冊中心的詳情請參閱說明文件。 filter.type=類型 filter.type.all=所有 filter.no_result=沒有篩選結果。 @@ -3060,34 +3061,42 @@ dependency.id=ID dependency.version=版本 composer.registry=在您的 ~/.composer/config.json 檔設定此註冊中心: composer.install=執行下列命令以使用 Composer 安裝此套件: +composer.documentation=關於 Composer registry 的詳情請參閱說明文件。 composer.dependencies=相依性 composer.dependencies.development=開發相依性 conan.details.repository=儲存庫 conan.registry=透過下列命令設定此註冊中心: conan.install=執行下列命令以使用 Conan 安裝此套件: +conan.documentation=關於 Conan registry 的詳情請參閱說明文件。 container.details.type=映像檔類型 container.details.platform=平台 container.details.repository_site=儲存庫網站 container.details.documentation_site=文件網站 container.pull=透過下列命令拉取映像檔: +container.documentation=關於 Container registry 的詳情請參閱說明文件。 container.multi_arch=作業系統 / 架構 container.layers=映像檔 Layers container.labels=標籤 container.labels.key=鍵 container.labels.value=值 generic.download=透過下列命令下載套件: +generic.documentation=關於通用 registry 的詳情請參閱說明文件。 helm.registry=透過下列命令設定此註冊中心: helm.install=執行下列命令安裝此套件: +helm.documentation=關於 Helm registry 的詳情請參閱說明文件。 maven.registry=在您的 pom.xml 檔設定此註冊中心: maven.install=若要使用此套件,請在您 pom.xml 檔的 dependencies 段落加入下列內容: maven.install2=透過下列命令執行: maven.download=透過下列命令下載相依性: +maven.documentation=關於 Maven registry 的詳情請參閱說明文件。 nuget.registry=透過下列命令設定此註冊中心: nuget.install=執行下列命令以使用 NuGet 安裝此套件: +nuget.documentation=關於 NuGet registry 的詳情請參閱說明文件。 nuget.dependency.framework=目標框架 npm.registry=在您的 .npmrc 檔設定此註冊中心: npm.install=執行下列命令以使用 npm 安裝此套件: npm.install2=或將它加到 package.json 檔: +npm.documentation=關於 npm registry 的詳情請參閱說明文件。 npm.dependencies=相依性 npm.dependencies.development=開發相依性 npm.dependencies.peer=Peer 相依性 @@ -3095,12 +3104,14 @@ npm.dependencies.optional=選用相依性 npm.details.tag=標籤 pypi.requires=需要 Python pypi.install=執行下列命令以使用 pip 安裝此套件: +pypi.documentation=關於 PyPI registry 的詳情請參閱說明文件。 rubygems.install=執行下列命令以使用 gem 安裝此套件: rubygems.install2=或將它加到 Gemfile: rubygems.dependencies.runtime=執行階段相依性 rubygems.dependencies.development=開發相依性 rubygems.required.ruby=需要的 Ruby 版本 rubygems.required.rubygems=需要的 RubyGem 版本 +rubygems.documentation=關於 RubyGems registry 的詳情請參閱說明文件。 settings.link=連結此套件到儲存庫 settings.link.description=如果您將套件連結到儲存庫,該套件會顯示在儲存庫的套件清單。 settings.link.select=選擇儲存庫 @@ -3108,6 +3119,8 @@ settings.link.button=更新儲存庫連結 settings.link.success=儲存庫連結更新成功。 settings.link.error=儲存庫連結更新失敗。 settings.delete=刪除套件 +settings.delete.description=刪除套件是永久且不可還原的。 +settings.delete.notice=您正要刪除 %s (%s),此動作是無法還原的,您確定嗎? settings.delete.success=已刪除該套件。 settings.delete.error=刪除套件失敗。 From e3e06d13afdd882ca5934fde77217ff9554354c4 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Fri, 17 Jun 2022 04:03:03 +0800 Subject: [PATCH 08/35] fix permission check for delete tag (#19985) fix #19970 by the way, fix some error response about protected tags. Signed-off-by: a1012112796 <1012112796@qq.com> --- routers/api/v1/repo/release.go | 6 ++++++ routers/api/v1/repo/release_tags.go | 7 +++++++ routers/api/v1/repo/tag.go | 14 ++++++++++++++ routers/web/repo/branch.go | 6 ++++++ routers/web/repo/release.go | 6 +++++- services/release/release.go | 14 ++++++++++++++ templates/swagger/v1_json.tmpl | 12 ++++++++++++ 7 files changed, 64 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index e454b418bb182..8dfe7e06d26f8 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -345,6 +345,8 @@ func DeleteRelease(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "405": + // "$ref": "#/responses/empty" id := ctx.ParamsInt64(":id") rel, err := models.GetReleaseByID(ctx, id) @@ -358,6 +360,10 @@ func DeleteRelease(ctx *context.APIContext) { return } if err := release_service.DeleteReleaseByID(ctx, id, ctx.Doer, false); err != nil { + if models.IsErrProtectedTagName(err) { + ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag") + return + } ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) return } diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index a737bcf1c8f4c..73dee73e1a743 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -92,6 +92,8 @@ func DeleteReleaseByTag(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "405": + // "$ref": "#/responses/empty" tag := ctx.Params(":tag") @@ -111,7 +113,12 @@ func DeleteReleaseByTag(ctx *context.APIContext) { } if err = releaseservice.DeleteReleaseByID(ctx, release.ID, ctx.Doer, false); err != nil { + if models.IsErrProtectedTagName(err) { + ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag") + return + } ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) + return } ctx.Status(http.StatusNoContent) diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 894291275400b..433d823c7ebc0 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -176,6 +176,8 @@ func CreateTag(ctx *context.APIContext) { // "$ref": "#/responses/Tag" // "404": // "$ref": "#/responses/notFound" + // "405": + // "$ref": "#/responses/empty" // "409": // "$ref": "#/responses/conflict" form := web.GetForm(ctx).(*api.CreateTagOption) @@ -196,6 +198,11 @@ func CreateTag(ctx *context.APIContext) { ctx.Error(http.StatusConflict, "tag exist", err) return } + if models.IsErrProtectedTagName(err) { + ctx.Error(http.StatusMethodNotAllowed, "CreateNewTag", "user not allowed to create protected tag") + return + } + ctx.InternalServerError(err) return } @@ -236,6 +243,8 @@ func DeleteTag(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "405": + // "$ref": "#/responses/empty" // "409": // "$ref": "#/responses/conflict" tagName := ctx.Params("*") @@ -256,7 +265,12 @@ func DeleteTag(ctx *context.APIContext) { } if err = releaseservice.DeleteReleaseByID(ctx, tag.ID, ctx.Doer, true); err != nil { + if models.IsErrProtectedTagName(err) { + ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag") + return + } ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) + return } ctx.Status(http.StatusNoContent) diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 4bd2af4e8e4da..84970a96a10dd 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -373,6 +373,12 @@ func CreateBranch(ctx *context.Context) { err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName) } if err != nil { + if models.IsErrProtectedTagName(err) { + ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) + ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) + return + } + if models.IsErrTagAlreadyExists(err) { e := err.(models.ErrTagAlreadyExists) ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName)) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index fba3ef7a064cc..666294631ced5 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -519,7 +519,11 @@ func DeleteTag(ctx *context.Context) { func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) { if err := releaseservice.DeleteReleaseByID(ctx, ctx.FormInt64("id"), ctx.Doer, isDelTag); err != nil { - ctx.Flash.Error("DeleteReleaseByID: " + err.Error()) + if models.IsErrProtectedTagName(err) { + ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) + } else { + ctx.Flash.Error("DeleteReleaseByID: " + err.Error()) + } } else { if isDelTag { ctx.Flash.Success(ctx.Tr("repo.release.deletion_tag_success")) diff --git a/services/release/release.go b/services/release/release.go index a3d7d47201d02..6fa966de1f352 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -294,6 +294,20 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del } if delTag { + protectedTags, err := git_model.GetProtectedTags(rel.RepoID) + if err != nil { + return fmt.Errorf("GetProtectedTags: %v", err) + } + isAllowed, err := git_model.IsUserAllowedToControlTag(protectedTags, rel.TagName, rel.PublisherID) + if err != nil { + return err + } + if !isAllowed { + return models.ErrProtectedTagName{ + TagName: rel.TagName, + } + } + if stdout, _, err := git.NewCommand(ctx, "tag", "-d", rel.TagName). SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)). RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ecc17b51c8af1..4da8b12af4bb1 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -8960,6 +8960,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "405": { + "$ref": "#/responses/empty" } } } @@ -9043,6 +9046,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "405": { + "$ref": "#/responses/empty" } } }, @@ -9811,6 +9817,9 @@ "404": { "$ref": "#/responses/notFound" }, + "405": { + "$ref": "#/responses/empty" + }, "409": { "$ref": "#/responses/conflict" } @@ -9898,6 +9907,9 @@ "404": { "$ref": "#/responses/notFound" }, + "405": { + "$ref": "#/responses/empty" + }, "409": { "$ref": "#/responses/conflict" } From 9068c784c8af1c76f88f94ada1bf5ee21c3cd25b Mon Sep 17 00:00:00 2001 From: oGi4i Date: Fri, 17 Jun 2022 01:29:54 +0300 Subject: [PATCH 09/35] Use DisplayName() instead of FullName in Oauth provider (#19991) Use DisplayName() in Oauth as this provides a fallback if FullName is not set. Closes #19382 --- routers/web/auth/oauth.go | 2 +- routers/web/auth/oauth_test.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index f646615968cc2..d868b05a44a25 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -215,7 +215,7 @@ func newAccessTokenResponse(ctx stdContext.Context, grant *auth.OAuth2Grant, ser Nonce: grant.Nonce, } if grant.ScopeContains("profile") { - idToken.Name = user.FullName + idToken.Name = user.GetDisplayName() idToken.PreferredUsername = user.Name idToken.Profile = user.HTMLURL() idToken.Picture = user.AvatarLink() diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go index 5a09a951054ed..57f2477dba3e6 100644 --- a/routers/web/auth/oauth_test.go +++ b/routers/web/auth/oauth_test.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/auth/source/oauth2" "github.com/golang-jwt/jwt/v4" @@ -64,6 +65,24 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { assert.NoError(t, err) assert.Len(t, grants, 1) + // Scopes: openid profile email + oidcToken = createAndParseToken(t, grants[0]) + assert.Equal(t, user.Name, oidcToken.Name) + assert.Equal(t, user.Name, oidcToken.PreferredUsername) + assert.Equal(t, user.HTMLURL(), oidcToken.Profile) + assert.Equal(t, user.AvatarLink(), oidcToken.Picture) + assert.Equal(t, user.Website, oidcToken.Website) + assert.Equal(t, user.UpdatedUnix, oidcToken.UpdatedAt) + assert.Equal(t, user.Email, oidcToken.Email) + assert.Equal(t, user.IsActive, oidcToken.EmailVerified) + + // set DefaultShowFullName to true + oldDefaultShowFullName := setting.UI.DefaultShowFullName + setting.UI.DefaultShowFullName = true + defer func() { + setting.UI.DefaultShowFullName = oldDefaultShowFullName + }() + // Scopes: openid profile email oidcToken = createAndParseToken(t, grants[0]) assert.Equal(t, user.FullName, oidcToken.Name) From 1e05adfc3eeeeee04be48c683185886fa4b00dee Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 17 Jun 2022 12:52:06 +0800 Subject: [PATCH 10/35] Delete duplicated update btn on pull request view page (#19993) Fix #19987 --- templates/repo/issue/view_content/pull.tmpl | 40 +-------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index d2282f07f6e31..5a23bfa33b164 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -283,7 +283,7 @@ {{end}} {{end}} - {{if (gt .Issue.PullRequest.CommitsBehind 0)}} + {{if and (gt .Issue.PullRequest.CommitsBehind 0) (not .Issue.IsClosed) (not .Issue.PullRequest.IsChecking) (not .IsPullFilesConflicted) (not .IsPullRequestBroken) (not $canAutoMerge)}}
@@ -481,44 +481,6 @@ {{end}} {{end}} - {{if and (gt .Issue.PullRequest.CommitsBehind 0) (not .Issue.IsClosed) (not .Issue.PullRequest.IsChecking) (not .IsPullFilesConflicted) (not .IsPullRequestBroken) (not $canAutoMerge)}} -
-
- {{svg "octicon-alert"}} - {{$.i18n.Tr "repo.pulls.outdated_with_base_branch"}} -
-
- {{if and .UpdateAllowed .UpdateByRebaseAllowed }} -
-
- - - -
-
- {{end}} - {{if and .UpdateAllowed (not .UpdateByRebaseAllowed)}} -
- {{.CsrfTokenHtml}} - -
- {{end}} -
-
- {{end}} - {{if $.StillCanManualMerge}}