From 9a15267871dcb1f1f8b39115b8e68ed1f42bf21d Mon Sep 17 00:00:00 2001 From: Earl Warren <109468362+earl-warren@users.noreply.github.com> Date: Sat, 16 Dec 2023 13:17:39 +0100 Subject: [PATCH 1/6] Initalize stroage for orphaned repository doctor (#28487) - When a repository is orphaned and has objects stored in any of the storages such as repository avatar or attachments the delete function would error, because the storage module wasn't initalized. - Add code to initialize the storage module. Refs: https://codeberg.org/forgejo/forgejo/pulls/1954 Co-authored-by: Gusted --- modules/doctor/repository.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/doctor/repository.go b/modules/doctor/repository.go index b3a03aa422d22..6c33426636e7b 100644 --- a/modules/doctor/repository.go +++ b/modules/doctor/repository.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/storage" repo_service "code.gitea.io/gitea/services/repository" "xorm.io/builder" @@ -31,6 +32,10 @@ func countOrphanedRepos(ctx context.Context) (int64, error) { // deleteOrphanedRepos delete repository where user of owner_id do not exist func deleteOrphanedRepos(ctx context.Context) (int64, error) { + if err := storage.Init(); err != nil { + return 0, err + } + batchSize := db.MaxBatchInsertSize("repository") e := db.GetEngine(ctx) var deleted int64 From 72529d6e64636fd83e24e709fb8bb0d383509f7f Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sun, 17 Dec 2023 00:26:56 +0000 Subject: [PATCH 2/6] [skip ci] Updated translations via Crowdin --- options/locale/locale_ja-JP.ini | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index bd2a2ea4794d7..7a7dcea8f8988 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -17,6 +17,7 @@ template=テンプレート language=言語 notifications=通知 active_stopwatch=進行中のタイムトラッカー +tracked_time_summary=イシューリストのフィルタに基づき集計したトラッキング時間 create_new=作成… user_profile_and_more=プロフィールと設定… signed_in_as=サインイン済み @@ -359,6 +360,7 @@ disable_register_prompt=登録は無効になっています。 サイト管理 disable_register_mail=登録でのメール確認は無効になっています。 manual_activation_only=アクティベーションを完了するにはサイト管理者に連絡してください。 remember_me=このデバイスで自動サインイン +remember_me.compromised=ログイントークンはもう有効ではなく、アカウントが侵害されたことを示している可能性があります。 異常なアクティビティがないかアカウントを確認してください。 forgot_password_title=パスワードを忘れた forgot_password=パスワードをお忘れですか? sign_up_now=アカウントが必要ですか? 今すぐ登録しましょう。 @@ -623,11 +625,11 @@ applications=アプリケーション orgs=組織の管理 repos=リポジトリ delete=アカウントを削除 -twofa=2要素認証 +twofa=2要素認証 (TOTP) account_link=連携アカウント organization=組織 uid=UID -webauthn=セキュリティキー +webauthn=2要素認証 (セキュリティキー) public_profile=公開プロフィール biography_placeholder=自己紹介してください!(Markdownを使うことができます) @@ -861,22 +863,23 @@ revoke_oauth2_grant=アクセス権の取り消し revoke_oauth2_grant_description=このサードパーティ アプリケーションのアクセス権を取り消し、アプリケーションがあなたのデータへアクセスすることを防ぎます。 続行しますか? revoke_oauth2_grant_success=アクセス権を取り消しました。 -twofa_desc=2要素認証はアカウントのセキュリティを強化します。 +twofa_desc=パスワードの盗難からアカウントを守るために、スマートフォンや他のデバイスを使用して、時間ベースのワンタイムパスワード("TOTP")を受け取ることができます。 +twofa_recovery_tip=デバイスを紛失した場合は、一回限りのリカバリキーを使用してアカウントへのアクセスを回復することができます。 twofa_is_enrolled=このアカウントは2要素認証が有効になっています。 twofa_not_enrolled=このアカウントは2要素認証が設定されていません。 twofa_disable=2要素認証を無効にする -twofa_scratch_token_regenerate=スクラッチトークンを再生成 -twofa_scratch_token_regenerated=あなたのスクラッチトークンは %s になりました。 安全な場所に保管してください。 二度と表示されません。 +twofa_scratch_token_regenerate=一回限りのリカバリキーを再生成 +twofa_scratch_token_regenerated=あなたの一回限りのリカバリキーは %s になりました。 安全な場所に保管してください。 これは二度と表示されません。 twofa_enroll=2要素認証の開始 twofa_disable_note=2要素認証は必要に応じて無効にできます。 twofa_disable_desc=2要素認証を無効にするとアカウントのセキュリティが低下します。 続行しますか? -regenerate_scratch_token_desc=スクラッチトークンを紛失した場合やサインインで使用済みとなった場合は、ここでリセットできます。 +regenerate_scratch_token_desc=リカバリキーを紛失した場合や、すでにサインインに使用済みの場合は、ここでリセットできます。 twofa_disabled=2要素認証を無効にしました。 scan_this_image=この画像を認証アプリケーションで読み取ってください。 or_enter_secret=またはシークレット文字列を入力: %s then_enter_passcode=次に、アプリケーションに表示されているパスコードを入力します。 passcode_invalid=パスコードが間違っています。 再度お試しください。 -twofa_enrolled=あなたのアカウントに2要素認証が設定されました。 スクラッチトークン (%s) は一度しか表示しませんので安全な場所に保存してください! +twofa_enrolled=あなたのアカウントは正常に登録されました。 一回限りのリカバリキー (%s) は安全な場所に保存してください。 これは二度と表示されません。 twofa_failed_get_secret=シークレットが取得できません。 webauthn_desc=セキュリティキーは暗号化キーを内蔵するハードウェア ・ デバイスです。 2要素認証に使用できます。 セキュリティキーはWebAuthn Authenticator規格をサポートしている必要があります。 @@ -884,6 +887,8 @@ webauthn_register_key=セキュリティキーを追加 webauthn_nickname=ニックネーム webauthn_delete_key=セキュリティキーの登録解除 webauthn_delete_key_desc=セキュリティキーの登録を解除すると、今後そのセキュリティキーでサインインすることはできなくなります。 続行しますか? +webauthn_key_loss_warning=セキュリティキーを紛失すると、アカウントへのアクセスを失います。 +webauthn_alternative_tip=もうひとつ別の認証方法も設定すると良いかもしれません。 manage_account_links=連携アカウントの管理 manage_account_links_desc=これらの外部アカウントがGiteaアカウントと連携されています。 @@ -920,6 +925,7 @@ visibility.private=プライベート visibility.private_tooltip=あなたが参加した組織のメンバーのみに表示されます [repo] +new_repo_helper=リポジトリには、プロジェクトのすべてのファイルとリビジョン履歴が入ります。 すでにほかの場所でホストしていますか? リポジトリを移行 もどうぞ。 owner=オーナー owner_helper=リポジトリ数の上限により、一部の組織はドロップダウンに表示されない場合があります。 repo_name=リポジトリ名 @@ -1782,6 +1788,8 @@ pulls.status_checks_failure=失敗したステータスチェックがありま pulls.status_checks_error=ステータスチェックによりエラーが出ています pulls.status_checks_requested=必須 pulls.status_checks_details=詳細 +pulls.status_checks_hide_all=すべてのチェックを隠す +pulls.status_checks_show_all=すべてのチェックを表示 pulls.update_branch=マージでブランチを更新 pulls.update_branch_rebase=リベースでブランチを更新 pulls.update_branch_success=ブランチの更新が成功しました @@ -1790,6 +1798,11 @@ pulls.outdated_with_base_branch=このブランチはベースブランチに対 pulls.close=プルリクエストをクローズ pulls.closed_at=`がプルリクエストをクローズ %[2]s` pulls.reopened_at=`がプルリクエストを再オープン %[2]s` +pulls.cmd_instruction_hint=`コマンドラインの手順を表示します。` +pulls.cmd_instruction_checkout_title=チェックアウト +pulls.cmd_instruction_checkout_desc=プロジェクトリポジトリから新しいブランチをチェックアウトし、変更内容をテストします。 +pulls.cmd_instruction_merge_title=マージ +pulls.cmd_instruction_merge_desc=変更内容をマージして、Giteaに反映します。 pulls.clear_merge_message=マージメッセージをクリア pulls.clear_merge_message_hint=マージメッセージのクリアは、コミットメッセージの除去だけを行います。 生成されたGitトレーラー("Co-Authored-By …" 等)はそのまま残ります。 @@ -2301,6 +2314,7 @@ settings.dismiss_stale_approvals_desc=プルリクエストの内容を変える settings.require_signed_commits=コミット署名必須 settings.require_signed_commits_desc=署名されていない場合、または署名が検証できなかった場合は、このブランチへのプッシュを拒否します。 settings.protect_branch_name_pattern=保護ブランチ名のパターン +settings.protect_branch_name_pattern_desc=保護ブランチ名のパターン。書き方については ドキュメント を参照してください。例: main, release/** settings.protect_patterns=パターン settings.protect_protected_file_patterns=保護されるファイルのパターン (セミコロン';'で区切る): settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については github.com/gobwas/glob を参照してください。 例: .drone.yml, /docs/**/*.txt @@ -2846,6 +2860,7 @@ emails.updated=メール設定を更新しました emails.not_updated=メール設定の更新に失敗しました: %v emails.duplicate_active=メールアドレスは別のユーザーが既に使用中です。 emails.change_email_header=メール設定の更新 +emails.change_email_text=このメールアドレスで更新してもよろしいですか? orgs.org_manage_panel=組織の管理 orgs.name=名称 @@ -2870,6 +2885,7 @@ packages.package_manage_panel=パッケージ管理 packages.total_size=合計サイズ: %s packages.unreferenced_size=非参照サイズ: %s packages.cleanup=期限切れデータを掃除する +packages.cleanup.success=期限切れのデータを正常にクリーンアップしました packages.owner=オーナー packages.creator=作成者 packages.name=名前 @@ -3516,6 +3532,7 @@ runs.actors_no_select=すべてのアクター runs.status_no_select=すべてのステータス runs.no_results=一致する結果はありません。 runs.no_runs=ワークフローはまだ実行されていません。 +runs.empty_commit_message=(空のコミットメッセージ) workflow.disable=ワークフローを無効にする workflow.disable_success=ワークフロー '%s' が無効になりました。 From 7fb6b5147038649bfaa26147a6173cf99b322a30 Mon Sep 17 00:00:00 2001 From: The Magician <142242365+TheMagician23@users.noreply.github.com> Date: Sun, 17 Dec 2023 04:33:37 +0000 Subject: [PATCH 3/6] Remove duplicate option in admin screen and now-unused translation keys (#28492) Resolves https://github.com/go-gitea/gitea/issues/28451. This change follows the recommendation by wxiaoguang to remove the "Disable Minimum Key Size Check" from the "Service Configuration" section of the UI, because this option belongs to the "SSH Configuration" section of the administration menu and already has a functioning indicator in that section of the UI. --------- Co-authored-by: wxiaoguang --- options/locale/locale_en-US.ini | 1 - templates/admin/config.tmpl | 2 -- 2 files changed, 3 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4f18606a45267..e57dd7794e4db 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3080,7 +3080,6 @@ config.enable_openid_signin = Enable OpenID Sign-In config.show_registration_button = Show Register Button config.require_sign_in_view = Require Sign-In to View Pages config.mail_notify = Enable Email Notifications -config.disable_key_size_check = Disable Minimum Key Size Check config.enable_captcha = Enable CAPTCHA config.active_code_lives = Active Code Lives config.reset_password_code_lives = Recover Account Code Expiry Time diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 7eb9d086e61cc..1cc4b7bb09ae5 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -151,8 +151,6 @@
{{if .Service.RequireSignInView}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
{{ctx.Locale.Tr "admin.config.mail_notify"}}
{{if .Service.EnableNotifyMail}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
-
{{ctx.Locale.Tr "admin.config.disable_key_size_check"}}
-
{{if .SSH.MinimumKeySizeCheck}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
{{ctx.Locale.Tr "admin.config.enable_captcha"}}
{{if .Service.EnableCaptcha}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
{{ctx.Locale.Tr "admin.config.default_keep_email_private"}}
From 408a4842240e7dd906e682196bd4254d6c76fcb9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 17 Dec 2023 19:56:08 +0800 Subject: [PATCH 4/6] Adjust object format interface (#28469) - Remove `ObjectFormatID` - Remove function `ObjectFormatFromID`. - Use `Sha1ObjectFormat` directly but not a pointer because it's an empty struct. - Store `ObjectFormatName` in `repository` struct --- models/git/branch_test.go | 4 +- models/repo/repo.go | 6 +- modules/git/blame_test.go | 2 +- modules/git/commit.go | 2 +- modules/git/object_format.go | 79 +++++++++---------- modules/git/object_id.go | 33 +++----- modules/git/object_id_gogit.go | 2 +- modules/git/object_id_test.go | 2 +- modules/git/parse_gogit_test.go | 14 ++-- modules/git/parse_nogogit_test.go | 6 +- modules/git/ref.go | 2 +- modules/git/repo.go | 10 ++- modules/git/repo_commit_gogit.go | 4 +- modules/git/repo_compare.go | 2 +- modules/git/repo_compare_test.go | 4 +- modules/git/repo_index.go | 2 +- modules/git/repo_tag_test.go | 2 +- modules/git/tag.go | 4 +- modules/git/tag_test.go | 6 +- modules/repository/commits_test.go | 2 +- modules/repository/generate.go | 4 +- modules/repository/init.go | 4 +- routers/api/v1/repo/repo.go | 24 +++--- routers/api/v1/utils/git.go | 2 +- routers/private/hook_pre_receive.go | 6 +- routers/private/hook_verification.go | 2 +- routers/private/hook_verification_test.go | 4 +- routers/web/repo/branch.go | 2 +- routers/web/repo/compare.go | 2 +- routers/web/repo/githttp.go | 2 +- routers/web/repo/repo.go | 24 +++--- routers/web/repo/setting/webhook.go | 2 +- services/agit/agit.go | 4 +- services/convert/git_commit_test.go | 10 +-- services/forms/repo_form.go | 3 +- services/gitdiff/gitdiff.go | 6 +- services/migrations/common.go | 2 +- services/migrations/gitea_uploader_test.go | 2 +- services/mirror/mirror_pull.go | 2 +- services/packages/cargo/index.go | 2 +- services/pull/pull.go | 2 +- services/pull/temp_repo.go | 9 +-- services/release/release.go | 6 +- services/repository/branch.go | 2 +- services/repository/check.go | 2 +- services/repository/create.go | 42 +++++----- services/repository/files/cherry_pick.go | 3 +- services/repository/files/temp_repo.go | 4 +- services/repository/files/update.go | 3 +- services/repository/files/upload.go | 2 +- services/repository/push.go | 14 ++-- services/wiki/wiki.go | 2 +- services/wiki/wiki_test.go | 2 +- .../git_helper_for_declarative_test.go | 2 +- 54 files changed, 190 insertions(+), 202 deletions(-) diff --git a/models/git/branch_test.go b/models/git/branch_test.go index adcf9fd305ae8..8febc80f14747 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -20,6 +20,7 @@ import ( func TestAddDeletedBranch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.EqualValues(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName) firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1}) assert.True(t, firstBranch.IsDeleted) @@ -29,8 +30,9 @@ func TestAddDeletedBranch(t *testing.T) { secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo.ID, Name: "branch2"}) assert.True(t, secondBranch.IsDeleted) + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) commit := &git.Commit{ - ID: repo.ObjectFormat.MustIDFromString(secondBranch.CommitID), + ID: objectFormat.MustIDFromString(secondBranch.CommitID), CommitMessage: secondBranch.CommitMessage, Committer: &git.Signature{ When: secondBranch.CommitTime.AsLocalTime(), diff --git a/models/repo/repo.go b/models/repo/repo.go index f739ada307797..fb1849a4bb7fa 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -180,7 +180,7 @@ type Repository struct { IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` Topics []string `xorm:"TEXT JSON"` - ObjectFormat git.ObjectFormat `xorm:"-"` + ObjectFormatName string `xorm:"-"` TrustModel TrustModelType @@ -277,7 +277,9 @@ func (repo *Repository) AfterLoad() { repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns - repo.ObjectFormat = git.ObjectFormatFromID(git.Sha1) + // this is a temporary behaviour to support old repos, next step is to store the object format in the database + // and read from database so this line could be removed. To not depend on git module, we use a constant variable here + repo.ObjectFormatName = "sha1" } // LoadAttributes loads attributes of the repository. diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go index 0afc6d2a1f0f7..327edab767089 100644 --- a/modules/git/blame_test.go +++ b/modules/git/blame_test.go @@ -39,7 +39,7 @@ func TestReadingBlameOutput(t *testing.T) { } for _, bypass := range []bool{false, true} { - blameReader, err := CreateBlameReader(ctx, &Sha1ObjectFormat{}, "./tests/repos/repo5_pulls", commit, "README.md", bypass) + blameReader, err := CreateBlameReader(ctx, Sha1ObjectFormat, "./tests/repos/repo5_pulls", commit, "README.md", bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/commit.go b/modules/git/commit.go index a8b6c0e8f793e..5d960e92f3384 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -236,7 +236,7 @@ func (c *Commit) IsForcePush(oldCommitID string) (bool, error) { if err != nil { return false, err } - if oldCommitID == objectFormat.Empty().String() { + if oldCommitID == objectFormat.EmptyObjectID().String() { return false, nil } diff --git a/modules/git/object_format.go b/modules/git/object_format.go index 3c52de772bb37..ee7e659ed04dd 100644 --- a/modules/git/object_format.go +++ b/modules/git/object_format.go @@ -5,26 +5,17 @@ package git import ( "crypto/sha1" - "fmt" "regexp" - "strings" -) - -type ObjectFormatID int - -const ( - Sha1 ObjectFormatID = iota ) // sha1Pattern can be used to determine if a string is an valid sha var sha1Pattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`) type ObjectFormat interface { - ID() ObjectFormatID - String() string - - // Empty is the hash of empty git - Empty() ObjectID + // Name returns the name of the object format + Name() string + // EmptyObjectID creates a new empty ObjectID from an object format hash name + EmptyObjectID() ObjectID // EmptyTree is the hash of an empty tree EmptyTree() ObjectID // FullLength is the length of the hash's hex string @@ -35,67 +26,71 @@ type ObjectFormat interface { MustIDFromString(s string) ObjectID NewID(b []byte) (ObjectID, error) NewIDFromString(s string) (ObjectID, error) - NewEmptyID() ObjectID NewHasher() HasherInterface } -type Sha1ObjectFormat struct{} +type Sha1ObjectFormatImpl struct{} -func (*Sha1ObjectFormat) ID() ObjectFormatID { return Sha1 } -func (*Sha1ObjectFormat) String() string { return "sha1" } -func (*Sha1ObjectFormat) Empty() ObjectID { return &Sha1Hash{} } -func (*Sha1ObjectFormat) EmptyTree() ObjectID { - return &Sha1Hash{ +var ( + emptyObjectID = &Sha1Hash{} + emptyTree = &Sha1Hash{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04, } +) + +func (Sha1ObjectFormatImpl) Name() string { return "sha1" } +func (Sha1ObjectFormatImpl) EmptyObjectID() ObjectID { + return emptyObjectID +} + +func (Sha1ObjectFormatImpl) EmptyTree() ObjectID { + return emptyTree } -func (*Sha1ObjectFormat) FullLength() int { return 40 } -func (*Sha1ObjectFormat) IsValid(input string) bool { +func (Sha1ObjectFormatImpl) FullLength() int { return 40 } +func (Sha1ObjectFormatImpl) IsValid(input string) bool { return sha1Pattern.MatchString(input) } -func (*Sha1ObjectFormat) MustID(b []byte) ObjectID { +func (Sha1ObjectFormatImpl) MustID(b []byte) ObjectID { var id Sha1Hash copy(id[0:20], b) return &id } -func (h *Sha1ObjectFormat) MustIDFromString(s string) ObjectID { +func (h Sha1ObjectFormatImpl) MustIDFromString(s string) ObjectID { return MustIDFromString(h, s) } -func (h *Sha1ObjectFormat) NewID(b []byte) (ObjectID, error) { +func (h Sha1ObjectFormatImpl) NewID(b []byte) (ObjectID, error) { return IDFromRaw(h, b) } -func (h *Sha1ObjectFormat) NewIDFromString(s string) (ObjectID, error) { +func (h Sha1ObjectFormatImpl) NewIDFromString(s string) (ObjectID, error) { return genericIDFromString(h, s) } -func (*Sha1ObjectFormat) NewEmptyID() ObjectID { - return NewSha1() -} - -func (h *Sha1ObjectFormat) NewHasher() HasherInterface { +func (h Sha1ObjectFormatImpl) NewHasher() HasherInterface { return &Sha1Hasher{sha1.New()} } -func ObjectFormatFromID(id ObjectFormatID) ObjectFormat { - switch id { - case Sha1: - return &Sha1ObjectFormat{} - } +var Sha1ObjectFormat ObjectFormat = Sha1ObjectFormatImpl{} - return nil +var SupportedObjectFormats = []ObjectFormat{ + Sha1ObjectFormat, + // TODO: add sha256 } -func ObjectFormatFromString(hash string) (ObjectFormat, error) { - switch strings.ToLower(hash) { - case "sha1": - return &Sha1ObjectFormat{}, nil +func ObjectFormatFromName(name string) ObjectFormat { + for _, objectFormat := range SupportedObjectFormats { + if name == objectFormat.Name() { + return objectFormat + } } + return nil +} - return nil, fmt.Errorf("unknown hash type: %s", hash) +func IsValidObjectFormat(name string) bool { + return ObjectFormatFromName(name) != nil } diff --git a/modules/git/object_id.go b/modules/git/object_id.go index 21e1c67c646c2..a90683678a817 100644 --- a/modules/git/object_id.go +++ b/modules/git/object_id.go @@ -31,18 +31,15 @@ func (h *Sha1Hash) IsZero() bool { return bytes.Equal(empty[:], h[:]) } func (h *Sha1Hash) RawValue() []byte { return h[:] } -func (*Sha1Hash) Type() ObjectFormat { return &Sha1ObjectFormat{} } +func (*Sha1Hash) Type() ObjectFormat { return Sha1ObjectFormat } -func NewSha1() *Sha1Hash { - return &Sha1Hash{} -} +var _ ObjectID = &Sha1Hash{} -// NewHash is for generic implementations -func NewHash(hash string) (ObjectID, error) { - hash = strings.ToLower(hash) - switch hash { - case "sha1": - return &Sha1Hash{}, nil +// EmptyObjectID creates a new ObjectID from an object format hash name +func EmptyObjectID(objectFormatName string) (ObjectID, error) { + objectFormat := ObjectFormatFromName(objectFormatName) + if objectFormat != nil { + return objectFormat.EmptyObjectID(), nil } return nil, errors.New("unsupported hash type") @@ -50,7 +47,7 @@ func NewHash(hash string) (ObjectID, error) { func IDFromRaw(h ObjectFormat, b []byte) (ObjectID, error) { if len(b) != h.FullLength()/2 { - return h.Empty(), fmt.Errorf("length must be %d: %v", h.FullLength(), b) + return h.EmptyObjectID(), fmt.Errorf("length must be %d: %v", h.FullLength(), b) } return h.MustID(b), nil } @@ -63,24 +60,20 @@ func MustIDFromString(h ObjectFormat, s string) ObjectID { func genericIDFromString(h ObjectFormat, s string) (ObjectID, error) { s = strings.TrimSpace(s) if len(s) != h.FullLength() { - return h.Empty(), fmt.Errorf("length must be %d: %s", h.FullLength(), s) + return h.EmptyObjectID(), fmt.Errorf("length must be %d: %s", h.FullLength(), s) } b, err := hex.DecodeString(s) if err != nil { - return h.Empty(), err + return h.EmptyObjectID(), err } return h.NewID(b) } func IDFromString(hexHash string) (ObjectID, error) { - switch len(hexHash) { - case 40: - hashType := Sha1ObjectFormat{} - h, err := hashType.NewIDFromString(hexHash) - if err != nil { - return nil, err + for _, objectFormat := range SupportedObjectFormats { + if len(hexHash) == objectFormat.FullLength() { + return objectFormat.NewIDFromString(hexHash) } - return h, nil } return nil, fmt.Errorf("invalid hash hex string: '%s' len: %d", hexHash, len(hexHash)) diff --git a/modules/git/object_id_gogit.go b/modules/git/object_id_gogit.go index 50917f0552d54..0cebb0d50b5ff 100644 --- a/modules/git/object_id_gogit.go +++ b/modules/git/object_id_gogit.go @@ -12,7 +12,7 @@ import ( func ParseGogitHash(h plumbing.Hash) ObjectID { switch hash.Size { case 20: - return ObjectFormatFromID(Sha1).MustID(h[:]) + return Sha1ObjectFormat.MustID(h[:]) } return nil diff --git a/modules/git/object_id_test.go b/modules/git/object_id_test.go index c78a215755396..1ad40096a07b1 100644 --- a/modules/git/object_id_test.go +++ b/modules/git/object_id_test.go @@ -10,7 +10,7 @@ import ( ) func TestIsValidSHAPattern(t *testing.T) { - h := NewSha1().Type() + h := Sha1ObjectFormat assert.True(t, h.IsValid("fee1")) assert.True(t, h.IsValid("abc000")) assert.True(t, h.IsValid("9023902390239023902390239023902390239023")) diff --git a/modules/git/parse_gogit_test.go b/modules/git/parse_gogit_test.go index 7ba50cbff90d4..9755f81cce617 100644 --- a/modules/git/parse_gogit_test.go +++ b/modules/git/parse_gogit_test.go @@ -28,9 +28,9 @@ func TestParseTreeEntries(t *testing.T) { Input: "100644 blob 61ab7345a1a3bbc590068ccae37b8515cfc5843c 1022\texample/file2.txt\n", Expected: []*TreeEntry{ { - ID: ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"), + ID: Sha1ObjectFormat.MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"), gogitTreeEntry: &object.TreeEntry{ - Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()), + Hash: plumbing.Hash(Sha1ObjectFormat.MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()), Name: "example/file2.txt", Mode: filemode.Regular, }, @@ -44,9 +44,9 @@ func TestParseTreeEntries(t *testing.T) { "040000 tree 1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8 -\texample\n", Expected: []*TreeEntry{ { - ID: ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"), + ID: Sha1ObjectFormat.MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"), gogitTreeEntry: &object.TreeEntry{ - Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()), + Hash: plumbing.Hash(Sha1ObjectFormat.MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()), Name: "example/\n.txt", Mode: filemode.Symlink, }, @@ -54,10 +54,10 @@ func TestParseTreeEntries(t *testing.T) { sized: true, }, { - ID: ObjectFormatFromID(Sha1).MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"), + ID: Sha1ObjectFormat.MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"), sized: true, gogitTreeEntry: &object.TreeEntry{ - Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8").RawValue()), + Hash: plumbing.Hash(Sha1ObjectFormat.MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8").RawValue()), Name: "example", Mode: filemode.Dir, }, @@ -67,7 +67,7 @@ func TestParseTreeEntries(t *testing.T) { } for _, testCase := range testCases { - entries, err := ParseTreeEntries(ObjectFormatFromID(Sha1), []byte(testCase.Input)) + entries, err := ParseTreeEntries(Sha1ObjectFormat, []byte(testCase.Input)) assert.NoError(t, err) if len(entries) > 1 { fmt.Println(testCase.Expected[0].ID) diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go index 0b78c081cd6f7..36313e00f331c 100644 --- a/modules/git/parse_nogogit_test.go +++ b/modules/git/parse_nogogit_test.go @@ -12,7 +12,7 @@ import ( ) func TestParseTreeEntriesLong(t *testing.T) { - objectFormat := ObjectFormatFromID(Sha1) + objectFormat := Sha1ObjectFormat testCases := []struct { Input string @@ -66,7 +66,7 @@ func TestParseTreeEntriesLong(t *testing.T) { } func TestParseTreeEntriesShort(t *testing.T) { - objectFormat := ObjectFormatFromID(Sha1) + objectFormat := Sha1ObjectFormat testCases := []struct { Input string @@ -102,7 +102,7 @@ func TestParseTreeEntriesShort(t *testing.T) { func TestParseTreeEntriesInvalid(t *testing.T) { // there was a panic: "runtime error: slice bounds out of range" when the input was invalid: #20315 - entries, err := ParseTreeEntries(ObjectFormatFromID(Sha1), []byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af")) + entries, err := ParseTreeEntries(Sha1ObjectFormat, []byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af")) assert.Error(t, err) assert.Len(t, entries, 0) } diff --git a/modules/git/ref.go b/modules/git/ref.go index b96b4ababb412..ed801f20d5c34 100644 --- a/modules/git/ref.go +++ b/modules/git/ref.go @@ -205,7 +205,7 @@ func RefURL(repoURL, ref string) string { return repoURL + "/src/branch/" + refName case refFullName.IsTag(): return repoURL + "/src/tag/" + refName - case !ObjectFormatFromID(Sha1).IsValid(ref): + case !Sha1ObjectFormat.IsValid(ref): // assume they mean a branch return repoURL + "/src/branch/" + refName default: diff --git a/modules/git/repo.go b/modules/git/repo.go index c036a217eb25c..52e54715d6532 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -90,7 +90,7 @@ func GetObjectFormatOfRepo(ctx context.Context, repoPath string) (ObjectFormat, } // InitRepository initializes a new Git repository. -func InitRepository(ctx context.Context, repoPath string, bare bool, objectFormat ObjectFormat) error { +func InitRepository(ctx context.Context, repoPath string, bare bool, objectFormatName string) error { err := os.MkdirAll(repoPath, os.ModePerm) if err != nil { return err @@ -98,7 +98,13 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma cmd := NewCommand(ctx, "init") if SupportHashSha256 { - cmd.AddOptionValues("--object-format", objectFormat.String()) + if objectFormatName == "" { + objectFormatName = Sha1ObjectFormat.Name() + } + if !IsValidObjectFormat(objectFormatName) { + return fmt.Errorf("invalid object format: %s", objectFormatName) + } + cmd.AddOptionValues("--object-format", objectFormatName) } if bare { cmd.AddArguments("--bare") diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go index 893055bccdf6b..d0992fd385ae0 100644 --- a/modules/git/repo_commit_gogit.go +++ b/modules/git/repo_commit_gogit.go @@ -54,9 +54,9 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { if err != nil { if strings.Contains(err.Error(), "unknown revision or path") || strings.Contains(err.Error(), "fatal: Needed a single revision") { - return objectFormat.Empty(), ErrNotExist{commitID, ""} + return objectFormat.EmptyObjectID(), ErrNotExist{commitID, ""} } - return objectFormat.Empty(), err + return objectFormat.EmptyObjectID(), err } return objectFormat.NewIDFromString(actualCommitID) diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 8885df4f7088e..0e9a0c70d791b 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -284,7 +284,7 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error { // If base is the SHA of an empty tree (EmptyTreeSHA), it returns the files changes from the initial commit to the head commit func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) { cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z") - if base == repo.objectFormat.Empty().String() { + if base == repo.objectFormat.EmptyObjectID().String() { cmd.AddDynamicArguments(head) } else { cmd.AddDynamicArguments(base, head) diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 9bfaa5c02a564..526b21355075a 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -131,12 +131,12 @@ func TestGetCommitFilesChanged(t *testing.T) { files []string }{ { - repo.objectFormat.Empty().String(), + repo.objectFormat.EmptyObjectID().String(), "95bb4d39648ee7e325106df01a621c530863a653", []string{"file1.txt"}, }, { - repo.objectFormat.Empty().String(), + repo.objectFormat.EmptyObjectID().String(), "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", []string{"file2.txt"}, }, diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 6f43734655f35..e3b19bf036463 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -101,7 +101,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { for _, file := range filenames { if file != "" { buffer.WriteString("0 ") - buffer.WriteString(repo.objectFormat.Empty().String()) + buffer.WriteString(repo.objectFormat.EmptyObjectID().String()) buffer.WriteByte('\t') buffer.WriteString(file) buffer.WriteByte('\000') diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index c7699f4a7d2c6..48c1bc41c2375 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -194,7 +194,7 @@ func TestRepository_GetAnnotatedTag(t *testing.T) { } func TestRepository_parseTagRef(t *testing.T) { - sha1 := ObjectFormatFromID(Sha1) + sha1 := Sha1ObjectFormat tests := []struct { name string diff --git a/modules/git/tag.go b/modules/git/tag.go index 27358d74f8db8..c7d0d8aef92b2 100644 --- a/modules/git/tag.go +++ b/modules/git/tag.go @@ -35,8 +35,8 @@ func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) { // \n\n separate headers from message func parseTagData(objectFormat ObjectFormat, data []byte) (*Tag, error) { tag := new(Tag) - tag.ID = objectFormat.NewEmptyID() - tag.Object = objectFormat.NewEmptyID() + tag.ID = objectFormat.EmptyObjectID() + tag.Object = objectFormat.EmptyObjectID() tag.Tagger = &Signature{} // we now have the contents of the commit object. Let's investigate... nextline := 0 diff --git a/modules/git/tag_test.go b/modules/git/tag_test.go index 129c1e3a02303..f980b0c560c4f 100644 --- a/modules/git/tag_test.go +++ b/modules/git/tag_test.go @@ -22,7 +22,7 @@ tagger Lucas Michot 1484491741 +0100 `), tag: Tag{ Name: "", - ID: NewSha1(), + ID: Sha1ObjectFormat.EmptyObjectID(), Object: &Sha1Hash{0x3b, 0x11, 0x4a, 0xb8, 0x0, 0xc6, 0x43, 0x2a, 0xd4, 0x23, 0x87, 0xcc, 0xf6, 0xbc, 0x8d, 0x43, 0x88, 0xa2, 0x88, 0x5a}, Type: "commit", Tagger: &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484491741, 0)}, @@ -39,7 +39,7 @@ o ono`), tag: Tag{ Name: "", - ID: NewSha1(), + ID: Sha1ObjectFormat.EmptyObjectID(), Object: &Sha1Hash{0x7c, 0xdf, 0x42, 0xc0, 0xb1, 0xcc, 0x76, 0x3a, 0xb7, 0xe4, 0xc3, 0x3c, 0x47, 0xa2, 0x4e, 0x27, 0xc6, 0x6b, 0xfc, 0xcc}, Type: "commit", Tagger: &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484553735, 0)}, @@ -49,7 +49,7 @@ ono`), tag: Tag{ } for _, test := range testData { - tag, err := parseTagData(ObjectFormatFromID(Sha1), test.data) + tag, err := parseTagData(Sha1ObjectFormat, test.data) assert.NoError(t, err) assert.EqualValues(t, test.tag.ID, tag.ID) assert.EqualValues(t, test.tag.Object, tag.Object) diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index 57f0c90fc6c6e..afcb183d7237a 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -169,7 +169,7 @@ func TestListToPushCommits(t *testing.T) { When: now, } - hashType := git.ObjectFormatFromID(git.Sha1) + hashType := git.Sha1ObjectFormat const hexString1 = "0123456789abcdef0123456789abcdef01234567" hash1, err := hashType.NewIDFromString(hexString1) assert.NoError(t, err) diff --git a/modules/repository/generate.go b/modules/repository/generate.go index c143431b7c6a8..f8478b8c1852a 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -224,7 +224,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r } // FIXME: fix the hash - if err := git.InitRepository(ctx, tmpDir, false, git.ObjectFormatFromID(git.Sha1)); err != nil { + if err := git.InitRepository(ctx, tmpDir, false, git.Sha1ObjectFormat.Name()); err != nil { return err } @@ -358,7 +358,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ } // FIXME - fix the hash - if err = CheckInitRepository(ctx, owner.Name, generateRepo.Name, git.ObjectFormatFromID(git.Sha1)); err != nil { + if err = CheckInitRepository(ctx, owner.Name, generateRepo.Name, git.Sha1ObjectFormat.Name()); err != nil { return generateRepo, err } diff --git a/modules/repository/init.go b/modules/repository/init.go index a9b5aab16aab5..b90b234a73f80 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -188,7 +188,7 @@ func InitRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi return nil } -func CheckInitRepository(ctx context.Context, owner, name string, objectFormat git.ObjectFormat) (err error) { +func CheckInitRepository(ctx context.Context, owner, name, objectFormatName string) (err error) { // Somehow the directory could exist. repoPath := repo_model.RepoPath(owner, name) isExist, err := util.IsExist(repoPath) @@ -204,7 +204,7 @@ func CheckInitRepository(ctx context.Context, owner, name string, objectFormat g } // Init git bare new repository. - if err = git.InitRepository(ctx, repoPath, true, objectFormat); err != nil { + if err = git.InitRepository(ctx, repoPath, true, objectFormatName); err != nil { return fmt.Errorf("git.InitRepository: %w", err) } else if err = CreateDelegateHooks(repoPath); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 1767a7fa674d5..6eb2cc4227429 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -242,18 +242,18 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre } repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_service.CreateRepoOptions{ - Name: opt.Name, - Description: opt.Description, - IssueLabels: opt.IssueLabels, - Gitignores: opt.Gitignores, - License: opt.License, - Readme: opt.Readme, - IsPrivate: opt.Private, - AutoInit: opt.AutoInit, - DefaultBranch: opt.DefaultBranch, - TrustModel: repo_model.ToTrustModel(opt.TrustModel), - IsTemplate: opt.Template, - ObjectFormat: git.ObjectFormatFromID(git.Sha1), + Name: opt.Name, + Description: opt.Description, + IssueLabels: opt.IssueLabels, + Gitignores: opt.Gitignores, + License: opt.License, + Readme: opt.Readme, + IsPrivate: opt.Private, + AutoInit: opt.AutoInit, + DefaultBranch: opt.DefaultBranch, + TrustModel: repo_model.ToTrustModel(opt.TrustModel), + IsTemplate: opt.Template, + ObjectFormatName: git.Sha1ObjectFormat.Name(), }) if err != nil { if repo_model.IsErrRepoAlreadyExist(err) { diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index eb82c505440fe..dfb1a130c3711 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -81,7 +81,7 @@ func ConvertToObjectID(ctx gocontext.Context, repo *context.Repository, commitID gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath()) if err != nil { - return objectFormat.Empty(), fmt.Errorf("RepositoryFromContextOrOpen: %w", err) + return objectFormat.EmptyObjectID(), fmt.Errorf("RepositoryFromContextOrOpen: %w", err) } defer closer.Close() diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 8811809710116..90d8287f06fd2 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -147,7 +147,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r gitRepo := ctx.Repo.GitRepo objectFormat, _ := gitRepo.GetObjectFormat() - if branchName == repo.DefaultBranch && newCommitID == objectFormat.Empty().String() { + if branchName == repo.DefaultBranch && newCommitID == objectFormat.EmptyObjectID().String() { log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo) ctx.JSON(http.StatusForbidden, private.Response{ UserMsg: fmt.Sprintf("branch %s is the default branch and cannot be deleted", branchName), @@ -175,7 +175,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r // First of all we need to enforce absolutely: // // 1. Detect and prevent deletion of the branch - if newCommitID == objectFormat.Empty().String() { + if newCommitID == objectFormat.EmptyObjectID().String() { log.Warn("Forbidden: Branch: %s in %-v is protected from deletion", branchName, repo) ctx.JSON(http.StatusForbidden, private.Response{ UserMsg: fmt.Sprintf("branch %s is protected from deletion", branchName), @@ -184,7 +184,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r } // 2. Disallow force pushes to protected branches - if oldCommitID != objectFormat.Empty().String() { + if oldCommitID != objectFormat.EmptyObjectID().String() { output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) if err != nil { log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err) diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index 6725205cc6d2c..8b2d0dd848a9d 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -30,7 +30,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] var command *git.Command objectFormat, _ := repo.GetObjectFormat() - if oldCommitID == objectFormat.Empty().String() { + if oldCommitID == objectFormat.EmptyObjectID().String() { // When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all": // List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits // So, it only lists the new commits received, doesn't list the commits already present in the receiving repository diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go index 7263ebc4235d3..04445b8eaf36c 100644 --- a/routers/private/hook_verification_test.go +++ b/routers/private/hook_verification_test.go @@ -30,9 +30,9 @@ func TestVerifyCommits(t *testing.T) { verified bool }{ {"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true}, - {objectFormat.Empty().String(), "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit + {objectFormat.EmptyObjectID().String(), "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit {"9779d17a04f1e2640583d35703c62460b2d86e0a", "72920278f2f999e3005801e5d5b8ab8139d3641c", false}, - {objectFormat.Empty().String(), "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit + {objectFormat.EmptyObjectID().String(), "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit } for _, tc := range testCases { diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index b6de5bf800e2d..c543160f42040 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -158,7 +158,7 @@ func RestoreBranchPost(ctx *context.Context) { if err := repo_service.PushUpdate( &repo_module.PushUpdateOptions{ RefFullName: git.RefNameFromBranch(deletedBranch.Name), - OldCommitID: objectFormat.Empty().String(), + OldCommitID: objectFormat.EmptyObjectID().String(), NewCommitID: deletedBranch.CommitID, PusherID: ctx.Doer.ID, PusherName: ctx.Doer.Name, diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 5b1638d3f5f53..f3b95b68fe5a2 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -317,7 +317,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { ci.BaseBranch = baseCommit.ID.String() ctx.Data["BaseBranch"] = ci.BaseBranch baseIsCommit = true - } else if ci.BaseBranch == objectFormat.Empty().String() { + } else if ci.BaseBranch == objectFormat.EmptyObjectID().String() { if isSameRepo { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch)) } else { diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index dd47bd79d94e1..6d3dd5a3fe629 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -329,7 +329,7 @@ func dummyInfoRefs(ctx *context.Context) { } }() - if err := git.InitRepository(ctx, tmpDir, true, git.ObjectFormatFromID(git.Sha1)); err != nil { + if err := git.InitRepository(ctx, tmpDir, true, git.Sha1ObjectFormat.Name()); err != nil { log.Error("Failed to init bare repo for git-receive-pack cache: %v", err) return } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 7a2976f8dcaac..b16e28383629e 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -278,18 +278,18 @@ func CreatePost(ctx *context.Context) { } } else { repo, err = repo_service.CreateRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{ - Name: form.RepoName, - Description: form.Description, - Gitignores: form.Gitignores, - IssueLabels: form.IssueLabels, - License: form.License, - Readme: form.Readme, - IsPrivate: form.Private || setting.Repository.ForcePrivate, - DefaultBranch: form.DefaultBranch, - AutoInit: form.AutoInit, - IsTemplate: form.Template, - TrustModel: repo_model.ToTrustModel(form.TrustModel), - ObjectFormat: form.ObjectFormat, + Name: form.RepoName, + Description: form.Description, + Gitignores: form.Gitignores, + IssueLabels: form.IssueLabels, + License: form.License, + Readme: form.Readme, + IsPrivate: form.Private || setting.Repository.ForcePrivate, + DefaultBranch: form.DefaultBranch, + AutoInit: form.AutoInit, + IsTemplate: form.Template, + TrustModel: repo_model.ToTrustModel(form.TrustModel), + ObjectFormatName: form.ObjectFormatName, }) if err == nil { log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name) diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index 8c232a4cb8d43..ab3c70006f795 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -662,7 +662,7 @@ func TestWebhook(ctx *context.Context) { return } commit = &git.Commit{ - ID: objectFormat.NewEmptyID(), + ID: objectFormat.EmptyObjectID(), Author: ghost.NewGitSig(), Committer: ghost.NewGitSig(), CommitMessage: "This is a fake commit", diff --git a/services/agit/agit.go b/services/agit/agit.go index e354b9169a201..bc68372570402 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -39,7 +39,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. objectFormat, _ := gitRepo.GetObjectFormat() for i := range opts.OldCommitIDs { - if opts.NewCommitIDs[i] == objectFormat.Empty().String() { + if opts.NewCommitIDs[i] == objectFormat.EmptyObjectID().String() { results = append(results, private.HookProcReceiveRefResult{ OriginalRef: opts.RefFullNames[i], OldOID: opts.OldCommitIDs[i], @@ -153,7 +153,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. results = append(results, private.HookProcReceiveRefResult{ Ref: pr.GetGitRefName(), OriginalRef: opts.RefFullNames[i], - OldOID: objectFormat.Empty().String(), + OldOID: objectFormat.EmptyObjectID().String(), NewOID: opts.NewCommitIDs[i], }) continue diff --git a/services/convert/git_commit_test.go b/services/convert/git_commit_test.go index d8c1fdfed78a7..73cb5e8c7170b 100644 --- a/services/convert/git_commit_test.go +++ b/services/convert/git_commit_test.go @@ -19,12 +19,12 @@ import ( func TestToCommitMeta(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - sha1 := git.ObjectFormatFromID(git.Sha1) + sha1 := git.Sha1ObjectFormat signature := &git.Signature{Name: "Test Signature", Email: "test@email.com", When: time.Unix(0, 0)} tag := &git.Tag{ Name: "Test Tag", - ID: sha1.Empty(), - Object: sha1.Empty(), + ID: sha1.EmptyObjectID(), + Object: sha1.EmptyObjectID(), Type: "Test Type", Tagger: signature, Message: "Test Message", @@ -34,8 +34,8 @@ func TestToCommitMeta(t *testing.T) { assert.NotNil(t, commitMeta) assert.EqualValues(t, &api.CommitMeta{ - SHA: sha1.Empty().String(), - URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.Empty().String()), + SHA: sha1.EmptyObjectID().String(), + URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.EmptyObjectID().String()), Created: time.Unix(0, 0), }, commitMeta) } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index f6ef97dfdc3b6..86599000a5882 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -13,7 +13,6 @@ import ( issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" @@ -54,7 +53,7 @@ type CreateRepoForm struct { TrustModel string ForkSingleBranch string - ObjectFormat git.ObjectFormat + ObjectFormatName string } // Validate validates the fields diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 05d4a0555fc6c..75fc687c86a52 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1120,7 +1120,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi return nil, err } - if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String()) && commit.ParentCount() == 0 { + if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String()) && commit.ParentCount() == 0 { cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"). AddArguments(opts.WhitespaceBehavior...). AddDynamicArguments(objectFormat.EmptyTree().String()). @@ -1229,7 +1229,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi } diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID} - if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String() { + if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String() { diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID} } diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...) @@ -1267,7 +1267,7 @@ func GetPullDiffStats(gitRepo *git.Repository, opts *DiffOptions) (*PullDiffStat } diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID} - if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String() { + if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String() { diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID} } diff --git a/services/migrations/common.go b/services/migrations/common.go index 278c156b036cb..d88518899d052 100644 --- a/services/migrations/common.go +++ b/services/migrations/common.go @@ -49,7 +49,7 @@ func CheckAndEnsureSafePR(pr *base.PullRequest, commonCloneBaseURL string, g bas // SECURITY: SHAs Must be a SHA // FIXME: hash only a SHA1 - CommitType := git.ObjectFormatFromID(git.Sha1) + CommitType := git.Sha1ObjectFormat if pr.MergeCommitSHA != "" && !CommitType.IsValid(pr.MergeCommitSHA) { WarnAndNotice("PR #%d in %s has invalid MergeCommitSHA: %s", pr.Number, g, pr.MergeCommitSHA) pr.MergeCommitSHA = "" diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index b6c9b814772a5..3dec3a26fc4a9 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -232,7 +232,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { // fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) baseRef := "master" - assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false, fromRepo.ObjectFormat)) + assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false, fromRepo.ObjectFormatName)) err := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644)) diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index b3ecf2fc54e1a..6f03e14ab08bd 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -484,7 +484,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { } notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ RefFullName: result.refName, - OldCommitID: objectFormat.Empty().String(), + OldCommitID: objectFormat.EmptyObjectID().String(), NewCommitID: commitID, }, repo_module.NewPushCommits()) notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID) diff --git a/services/packages/cargo/index.go b/services/packages/cargo/index.go index 48bd0a4d80a25..9514e35bedd9a 100644 --- a/services/packages/cargo/index.go +++ b/services/packages/cargo/index.go @@ -271,7 +271,7 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re if !git.IsErrBranchNotExist(err) || !repo.IsEmpty { return err } - if err := t.Init(repo.ObjectFormat); err != nil { + if err := t.Init(repo.ObjectFormatName); err != nil { return err } } else { diff --git a/services/pull/pull.go b/services/pull/pull.go index a16d1be1c1cbd..6094a4ed31b9e 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -328,7 +328,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, if err == nil { for _, pr := range prs { objectFormat, _ := git.GetObjectFormatOfRepo(ctx, pr.BaseRepo.RepoPath()) - if newCommitID != "" && newCommitID != objectFormat.Empty().String() { + if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) if err != nil { log.Error("checkIfPRContentChanged: %v", err) diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index fde8673a2403f..36bdbde55c7a3 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -93,14 +93,8 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) baseRepoPath := pr.BaseRepo.RepoPath() headRepoPath := pr.HeadRepo.RepoPath() - objectFormat, err := git.GetObjectFormatOfRepo(ctx, baseRepoPath) - if err != nil { - log.Error("Unable to fetch ObjectFormat of repository %s: %v", baseRepoPath, err) - cancel() - return nil, nil, err - } - if err := git.InitRepository(ctx, tmpBasePath, false, objectFormat); err != nil { + if err := git.InitRepository(ctx, tmpBasePath, false, pr.BaseRepo.ObjectFormatName); err != nil { log.Error("Unable to init tmpBasePath for %-v: %v", pr, err) cancel() return nil, nil, err @@ -174,6 +168,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) } trackingBranch := "tracking" + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) // Fetch head branch var headBranch string if pr.Flow == issues_model.PullRequestFlowGithub { diff --git a/services/release/release.go b/services/release/release.go index 4cd520e82f965..fc91171fba6ae 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -89,14 +89,14 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel objectFormat, _ := gitRepo.GetObjectFormat() commits := repository.NewPushCommits() commits.HeadCommit = repository.CommitToPushCommit(commit) - commits.CompareURL = rel.Repo.ComposeCompareURL(objectFormat.Empty().String(), commit.ID.String()) + commits.CompareURL = rel.Repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), commit.ID.String()) refFullName := git.RefNameFromTag(rel.TagName) notify_service.PushCommits( ctx, rel.Publisher, rel.Repo, &repository.PushUpdateOptions{ RefFullName: refFullName, - OldCommitID: objectFormat.Empty().String(), + OldCommitID: objectFormat.EmptyObjectID().String(), NewCommitID: commit.ID.String(), }, commits) notify_service.CreateRef(ctx, rel.Publisher, rel.Repo, refFullName, commit.ID.String()) @@ -335,7 +335,7 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re &repository.PushUpdateOptions{ RefFullName: refName, OldCommitID: rel.Sha1, - NewCommitID: objectFormat.Empty().String(), + NewCommitID: objectFormat.EmptyObjectID().String(), }, repository.NewPushCommits()) notify_service.DeleteRef(ctx, doer, repo, refName) diff --git a/services/repository/branch.go b/services/repository/branch.go index b797917757ede..dca938444aa35 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -408,7 +408,7 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R &repo_module.PushUpdateOptions{ RefFullName: git.RefNameFromBranch(branchName), OldCommitID: commit.ID.String(), - NewCommitID: objectFormat.Empty().String(), + NewCommitID: objectFormat.EmptyObjectID().String(), PusherID: doer.ID, PusherName: doer.Name, RepoUserName: repo.OwnerName, diff --git a/services/repository/check.go b/services/repository/check.go index 23c4f79bf2045..b874ede51fdbc 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -192,7 +192,7 @@ func ReinitMissingRepositories(ctx context.Context) error { default: } log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) - if err := git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormat); err != nil { + if err := git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil { log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err) if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil { log.Error("CreateRepositoryNotice: %v", err2) diff --git a/services/repository/create.go b/services/repository/create.go index a41904eb7cdf0..bcf2c85c21817 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -27,23 +27,23 @@ import ( // CreateRepoOptions contains the create repository options type CreateRepoOptions struct { - Name string - Description string - OriginalURL string - GitServiceType api.GitServiceType - Gitignores string - IssueLabels string - License string - Readme string - DefaultBranch string - IsPrivate bool - IsMirror bool - IsTemplate bool - AutoInit bool - Status repo_model.RepositoryStatus - TrustModel repo_model.TrustModelType - MirrorInterval string - ObjectFormat git.ObjectFormat + Name string + Description string + OriginalURL string + GitServiceType api.GitServiceType + Gitignores string + IssueLabels string + License string + Readme string + DefaultBranch string + IsPrivate bool + IsMirror bool + IsTemplate bool + AutoInit bool + Status repo_model.RepositoryStatus + TrustModel repo_model.TrustModelType + MirrorInterval string + ObjectFormatName string } func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { @@ -135,7 +135,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, // InitRepository initializes README and .gitignore if needed. func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) { - if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name, opts.ObjectFormat); err != nil { + if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name, opts.ObjectFormatName); err != nil { return err } @@ -210,10 +210,6 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt opts.DefaultBranch = setting.Repository.DefaultBranch } - if opts.ObjectFormat == nil { - opts.ObjectFormat = git.ObjectFormatFromID(git.Sha1) - } - // Check if label template exist if len(opts.IssueLabels) > 0 { if _, err := repo_module.LoadTemplateLabelsByDisplayName(opts.IssueLabels); err != nil { @@ -239,7 +235,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt TrustModel: opts.TrustModel, IsMirror: opts.IsMirror, DefaultBranch: opts.DefaultBranch, - ObjectFormat: opts.ObjectFormat, + ObjectFormatName: opts.ObjectFormatName, } var rollbackRepo *repo_model.Repository diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index 0085e88d55274..e88ea16119799 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/pull" @@ -66,7 +67,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod } parent, err := commit.ParentID(0) if err != nil { - parent = repo.ObjectFormat.EmptyTree() + parent = git.ObjectFormatFromName(repo.ObjectFormatName).EmptyTree() } base, right := parent.String(), commit.ID.String() diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 0b5aaba1544ad..6a0b7b675c81e 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -77,8 +77,8 @@ func (t *TemporaryUploadRepository) Clone(branch string) error { } // Init the repository -func (t *TemporaryUploadRepository) Init(objectFormat git.ObjectFormat) error { - if err := git.InitRepository(t.ctx, t.basePath, false, objectFormat); err != nil { +func (t *TemporaryUploadRepository) Init(objectFormatName string) error { + if err := git.InitRepository(t.ctx, t.basePath, false, objectFormatName); err != nil { return err } gitRepo, err := git.OpenRepository(t.ctx, t.basePath) diff --git a/services/repository/files/update.go b/services/repository/files/update.go index d202717ef5706..dd8d9ee42563d 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -155,8 +155,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use if !git.IsErrBranchNotExist(err) || !repo.IsEmpty { return nil, err } - objectFormat, _ := gitRepo.GetObjectFormat() - if err := t.Init(objectFormat); err != nil { + if err := t.Init(repo.ObjectFormatName); err != nil { return nil, err } hasOldBranch = false diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go index 8be8773544779..61e38b55a3705 100644 --- a/services/repository/files/upload.go +++ b/services/repository/files/upload.go @@ -91,7 +91,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use if !git.IsErrBranchNotExist(err) || !repo.IsEmpty { return err } - if err = t.Init(repo.ObjectFormat); err != nil { + if err = t.Init(repo.ObjectFormatName); err != nil { return err } hasOldBranch = false diff --git a/services/repository/push.go b/services/repository/push.go index 3003933c34bcf..3bc7a78cb955d 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -111,7 +111,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { log.Trace("pushUpdates: %-v %s %s %s", repo, opts.OldCommitID, opts.NewCommitID, opts.RefFullName) if opts.IsNewRef() && opts.IsDelRef() { - return fmt.Errorf("old and new revisions are both %s", objectFormat.Empty()) + return fmt.Errorf("old and new revisions are both %s", objectFormat.EmptyObjectID()) } if opts.RefFullName.IsTag() { if pusher == nil || pusher.ID != opts.PusherID { @@ -131,7 +131,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { &repo_module.PushUpdateOptions{ RefFullName: git.RefNameFromTag(tagName), OldCommitID: opts.OldCommitID, - NewCommitID: objectFormat.Empty().String(), + NewCommitID: objectFormat.EmptyObjectID().String(), }, repo_module.NewPushCommits()) delTags = append(delTags, tagName) @@ -144,13 +144,13 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { commits := repo_module.NewPushCommits() commits.HeadCommit = repo_module.CommitToPushCommit(newCommit) - commits.CompareURL = repo.ComposeCompareURL(objectFormat.Empty().String(), opts.NewCommitID) + commits.CompareURL = repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), opts.NewCommitID) notify_service.PushCommits( ctx, pusher, repo, &repo_module.PushUpdateOptions{ RefFullName: opts.RefFullName, - OldCommitID: objectFormat.Empty().String(), + OldCommitID: objectFormat.EmptyObjectID().String(), NewCommitID: opts.NewCommitID, }, commits) @@ -234,7 +234,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } oldCommitID := opts.OldCommitID - if oldCommitID == objectFormat.Empty().String() && len(commits.Commits) > 0 { + if oldCommitID == objectFormat.EmptyObjectID().String() && len(commits.Commits) > 0 { oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1) if err != nil && !git.IsErrNotExist(err) { log.Error("unable to GetCommit %s from %-v: %v", oldCommitID, repo, err) @@ -250,11 +250,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } } - if oldCommitID == objectFormat.Empty().String() && repo.DefaultBranch != branch { + if oldCommitID == objectFormat.EmptyObjectID().String() && repo.DefaultBranch != branch { oldCommitID = repo.DefaultBranch } - if oldCommitID != objectFormat.Empty().String() { + if oldCommitID != objectFormat.EmptyObjectID().String() { commits.CompareURL = repo.ComposeCompareURL(oldCommitID, opts.NewCommitID) } else { commits.CompareURL = "" diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index ecda926ec1e2d..f98854c8dd5f6 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -36,7 +36,7 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error { return nil } - if err := git.InitRepository(ctx, repo.WikiPath(), true, git.ObjectFormatFromID(git.Sha1)); err != nil { + if err := git.InitRepository(ctx, repo.WikiPath(), true, repo.ObjectFormatName); err != nil { return fmt.Errorf("InitRepository: %w", err) } else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index 9981fb42583f3..277fa086ac13c 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -302,7 +302,7 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) { // Now create a temporaryDirectory tmpDir := t.TempDir() - err := git.InitRepository(git.DefaultContext, tmpDir, true, git.ObjectFormatFromID(git.Sha1)) + err := git.InitRepository(git.DefaultContext, tmpDir, true, git.Sha1ObjectFormat.Name()) assert.NoError(t, err) gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir) diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go index de671dec19e98..77fe07128e75a 100644 --- a/tests/integration/git_helper_for_declarative_test.go +++ b/tests/integration/git_helper_for_declarative_test.go @@ -120,7 +120,7 @@ func doGitCloneFail(u *url.URL) func(*testing.T) { func doGitInitTestRepository(dstPath string) func(*testing.T) { return func(t *testing.T) { // Init repository in dstPath - assert.NoError(t, git.InitRepository(git.DefaultContext, dstPath, false, git.ObjectFormatFromID(git.Sha1))) + assert.NoError(t, git.InitRepository(git.DefaultContext, dstPath, false, git.Sha1ObjectFormat.Name())) // forcibly set default branch to master _, _, err := git.NewCommand(git.DefaultContext, "symbolic-ref", "HEAD", git.BranchPrefix+"master").RunStdString(&git.RunOpts{Dir: dstPath}) assert.NoError(t, err) From 20929edc9962281e35a81756d76dd1caa5741ff8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 17 Dec 2023 22:38:54 +0800 Subject: [PATCH 5/6] Add option to disable ambiguous unicode characters detection (#28454) * Close #24483 * Close #28123 * Close #23682 * Close #23149 (maybe more) --- custom/conf/app.example.ini | 3 + .../config-cheat-sheet.en-us.md | 1 + modules/charset/escape.go | 59 ++++--------------- modules/charset/escape_stream.go | 2 +- modules/charset/escape_test.go | 52 +++++----------- modules/git/command.go | 11 +--- modules/highlight/highlight.go | 29 +++++---- modules/highlight/highlight_test.go | 32 ++++++---- modules/indexer/code/search.go | 3 +- modules/markup/orgmode/orgmode.go | 2 +- modules/setting/ui.go | 5 ++ modules/util/string.go | 14 ++++- routers/web/repo/blame.go | 3 +- routers/web/repo/view.go | 24 ++++---- services/gitdiff/gitdiff.go | 6 +- services/gitdiff/highlightdiff.go | 6 +- templates/repo/view_file.tmpl | 6 +- 17 files changed, 111 insertions(+), 147 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index e10c4f7582e91..9a5d19074595f 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1212,6 +1212,9 @@ LEVEL = Info ;; Max size of files to be displayed (default is 8MiB) ;MAX_DISPLAY_FILE_SIZE = 8388608 ;; +;; Detect ambiguous unicode characters in file contents and show warnings on the UI +;AMBIGUOUS_UNICODE_DETECTION = true +;; ;; Whether the email of the user should be shown in the Explore Users page ;SHOW_USER_EMAIL = true ;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 1fa8dacb67e25..9810dab49bc15 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -220,6 +220,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `THEMES`: **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes. regardless of the value of `DEFAULT_THEME`. - `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB) +- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI - `REACTIONS`: All available reactions users can choose on issues/prs and comments Values can be emoji alias (:smile:) or a unicode emoji. For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png diff --git a/modules/charset/escape.go b/modules/charset/escape.go index 5608836a4510e..92e417d1f7283 100644 --- a/modules/charset/escape.go +++ b/modules/charset/escape.go @@ -8,11 +8,12 @@ package charset import ( - "bufio" + "html/template" "io" "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/translation" ) @@ -20,20 +21,18 @@ import ( const RuneNBSP = 0xa0 // EscapeControlHTML escapes the unicode control sequences in a provided html document -func EscapeControlHTML(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) { +func EscapeControlHTML(html template.HTML, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output template.HTML) { sb := &strings.Builder{} - outputStream := &HTMLStreamerWriter{Writer: sb} - streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer) - - if err := StreamHTML(strings.NewReader(text), streamer); err != nil { - streamer.escaped.HasError = true - log.Error("Error whilst escaping: %v", err) - } - return streamer.escaped, sb.String() + escaped, _ = EscapeControlReader(strings.NewReader(string(html)), sb, locale, allowed...) // err has been handled in EscapeControlReader + return escaped, template.HTML(sb.String()) } -// EscapeControlReaders escapes the unicode control sequences in a provided reader of HTML content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte +// EscapeControlReader escapes the unicode control sequences in a provided reader of HTML content and writer in a locale and returns the findings as an EscapeStatus func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) { + if !setting.UI.AmbiguousUnicodeDetection { + _, err = io.Copy(writer, reader) + return &EscapeStatus{}, err + } outputStream := &HTMLStreamerWriter{Writer: writer} streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer) @@ -43,41 +42,3 @@ func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation. } return streamer.escaped, err } - -// EscapeControlStringReader escapes the unicode control sequences in a provided reader of string content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte. HTML line breaks are not inserted after every newline by this method. -func EscapeControlStringReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) { - bufRd := bufio.NewReader(reader) - outputStream := &HTMLStreamerWriter{Writer: writer} - streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer) - - for { - line, rdErr := bufRd.ReadString('\n') - if len(line) > 0 { - if err := streamer.Text(line); err != nil { - streamer.escaped.HasError = true - log.Error("Error whilst escaping: %v", err) - return streamer.escaped, err - } - } - if rdErr != nil { - if rdErr != io.EOF { - err = rdErr - } - break - } - } - return streamer.escaped, err -} - -// EscapeControlString escapes the unicode control sequences in a provided string and returns the findings as an EscapeStatus and the escaped string -func EscapeControlString(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) { - sb := &strings.Builder{} - outputStream := &HTMLStreamerWriter{Writer: sb} - streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer) - - if err := streamer.Text(text); err != nil { - streamer.escaped.HasError = true - log.Error("Error whilst escaping: %v", err) - } - return streamer.escaped, sb.String() -} diff --git a/modules/charset/escape_stream.go b/modules/charset/escape_stream.go index 03d4cfc0c17bc..3f08fd94a497f 100644 --- a/modules/charset/escape_stream.go +++ b/modules/charset/escape_stream.go @@ -64,7 +64,7 @@ func (e *escapeStreamer) Text(data string) error { until, next = nextIdxs[0]+pos, nextIdxs[1]+pos } - // from pos until until we know that the runes are not \r\t\n or even ' ' + // from pos until we know that the runes are not \r\t\n or even ' ' runes := make([]rune, 0, next-until) positions := make([]int, 0, next-until+1) diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go index f63c5c5c52b32..a353ced63169f 100644 --- a/modules/charset/escape_test.go +++ b/modules/charset/escape_test.go @@ -4,11 +4,14 @@ package charset import ( - "reflect" "strings" "testing" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" + + "github.com/stretchr/testify/assert" ) type escapeControlTest struct { @@ -132,22 +135,8 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`, }, } -func TestEscapeControlString(t *testing.T) { - for _, tt := range escapeControlTests { - t.Run(tt.name, func(t *testing.T) { - status, result := EscapeControlString(tt.text, &translation.MockLocale{}) - if !reflect.DeepEqual(*status, tt.status) { - t.Errorf("EscapeControlString() status = %v, wanted= %v", status, tt.status) - } - if result != tt.result { - t.Errorf("EscapeControlString()\nresult= %v,\nwanted= %v", result, tt.result) - } - }) - } -} - func TestEscapeControlReader(t *testing.T) { - // lets add some control characters to the tests + // add some control characters to the tests tests := make([]escapeControlTest, 0, len(escapeControlTests)*3) copy(tests, escapeControlTests) @@ -169,29 +158,20 @@ func TestEscapeControlReader(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - input := strings.NewReader(tt.text) output := &strings.Builder{} - status, err := EscapeControlReader(input, output, &translation.MockLocale{}) - result := output.String() - if err != nil { - t.Errorf("EscapeControlReader(): err = %v", err) - } - - if !reflect.DeepEqual(*status, tt.status) { - t.Errorf("EscapeControlReader() status = %v, wanted= %v", status, tt.status) - } - if result != tt.result { - t.Errorf("EscapeControlReader()\nresult= %v,\nwanted= %v", result, tt.result) - } + status, err := EscapeControlReader(strings.NewReader(tt.text), output, &translation.MockLocale{}) + assert.NoError(t, err) + assert.Equal(t, tt.status, *status) + assert.Equal(t, tt.result, output.String()) }) } } -func TestEscapeControlReader_panic(t *testing.T) { - bs := make([]byte, 0, 20479) - bs = append(bs, 'A') - for i := 0; i < 6826; i++ { - bs = append(bs, []byte("—")...) - } - _, _ = EscapeControlString(string(bs), &translation.MockLocale{}) +func TestSettingAmbiguousUnicodeDetection(t *testing.T) { + defer test.MockVariableValue(&setting.UI.AmbiguousUnicodeDetection, true)() + _, out := EscapeControlHTML("a test", &translation.MockLocale{}) + assert.EqualValues(t, `a test`, out) + setting.UI.AmbiguousUnicodeDetection = false + _, out = EscapeControlHTML("a test", &translation.MockLocale{}) + assert.EqualValues(t, `a test`, out) } diff --git a/modules/git/command.go b/modules/git/command.go index f095bb18bef75..9305ef6f928e7 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -14,7 +14,6 @@ import ( "os/exec" "strings" "time" - "unsafe" "code.gitea.io/gitea/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions "code.gitea.io/gitea/modules/log" @@ -389,15 +388,11 @@ func (r *runStdError) IsExitCode(code int) bool { return false } -func bytesToString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) // that's what Golang's strings.Builder.String() does (go/src/strings/builder.go) -} - // RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) { stdoutBytes, stderrBytes, err := c.RunStdBytes(opts) - stdout = bytesToString(stdoutBytes) - stderr = bytesToString(stderrBytes) + stdout = util.UnsafeBytesToString(stdoutBytes) + stderr = util.UnsafeBytesToString(stderrBytes) if err != nil { return stdout, stderr, &runStdError{err: err, stderr: stderr} } @@ -432,7 +427,7 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS err := c.Run(newOpts) stderr = stderrBuf.Bytes() if err != nil { - return nil, stderr, &runStdError{err: err, stderr: bytesToString(stderr)} + return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)} } // even if there is no err, there could still be some stderr output return stdoutBuf.Bytes(), stderr, nil diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index a67217e864675..d7ab3f7afd3e7 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -9,6 +9,7 @@ import ( "bytes" "fmt" gohtml "html" + "html/template" "io" "path/filepath" "strings" @@ -55,7 +56,7 @@ func NewContext() { } // Code returns a HTML version of code string with chroma syntax highlighting classes and the matched lexer name -func Code(fileName, language, code string) (string, string) { +func Code(fileName, language, code string) (output template.HTML, lexerName string) { NewContext() // diff view newline will be passed as empty, change to literal '\n' so it can be copied @@ -65,7 +66,7 @@ func Code(fileName, language, code string) (string, string) { } if len(code) > sizeLimit { - return code, "" + return template.HTML(template.HTMLEscapeString(code)), "" } var lexer chroma.Lexer @@ -102,13 +103,11 @@ func Code(fileName, language, code string) (string, string) { cache.Add(fileName, lexer) } - lexerName := formatLexerName(lexer.Config().Name) - - return CodeFromLexer(lexer, code), lexerName + return CodeFromLexer(lexer, code), formatLexerName(lexer.Config().Name) } // CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes -func CodeFromLexer(lexer chroma.Lexer, code string) string { +func CodeFromLexer(lexer chroma.Lexer, code string) template.HTML { formatter := html.New(html.WithClasses(true), html.WithLineNumbers(false), html.PreventSurroundingPre(true), @@ -120,23 +119,23 @@ func CodeFromLexer(lexer chroma.Lexer, code string) string { iterator, err := lexer.Tokenise(nil, code) if err != nil { log.Error("Can't tokenize code: %v", err) - return code + return template.HTML(template.HTMLEscapeString(code)) } // style not used for live site but need to pass something err = formatter.Format(htmlw, githubStyles, iterator) if err != nil { log.Error("Can't format code: %v", err) - return code + return template.HTML(template.HTMLEscapeString(code)) } _ = htmlw.Flush() // Chroma will add newlines for certain lexers in order to highlight them properly // Once highlighted, strip them here, so they don't cause copy/paste trouble in HTML output - return strings.TrimSuffix(htmlbuf.String(), "\n") + return template.HTML(strings.TrimSuffix(htmlbuf.String(), "\n")) } // File returns a slice of chroma syntax highlighted HTML lines of code and the matched lexer name -func File(fileName, language string, code []byte) ([]string, string, error) { +func File(fileName, language string, code []byte) ([]template.HTML, string, error) { NewContext() if len(code) > sizeLimit { @@ -183,14 +182,14 @@ func File(fileName, language string, code []byte) ([]string, string, error) { tokensLines := chroma.SplitTokensIntoLines(iterator.Tokens()) htmlBuf := &bytes.Buffer{} - lines := make([]string, 0, len(tokensLines)) + lines := make([]template.HTML, 0, len(tokensLines)) for _, tokens := range tokensLines { iterator = chroma.Literator(tokens...) err = formatter.Format(htmlBuf, githubStyles, iterator) if err != nil { return nil, "", fmt.Errorf("can't format code: %w", err) } - lines = append(lines, htmlBuf.String()) + lines = append(lines, template.HTML(htmlBuf.String())) htmlBuf.Reset() } @@ -198,9 +197,9 @@ func File(fileName, language string, code []byte) ([]string, string, error) { } // PlainText returns non-highlighted HTML for code -func PlainText(code []byte) []string { +func PlainText(code []byte) []template.HTML { r := bufio.NewReader(bytes.NewReader(code)) - m := make([]string, 0, bytes.Count(code, []byte{'\n'})+1) + m := make([]template.HTML, 0, bytes.Count(code, []byte{'\n'})+1) for { content, err := r.ReadString('\n') if err != nil && err != io.EOF { @@ -210,7 +209,7 @@ func PlainText(code []byte) []string { if content == "" && err == io.EOF { break } - s := gohtml.EscapeString(content) + s := template.HTML(gohtml.EscapeString(content)) m = append(m, s) } return m diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index 7a9887728f18d..659688bd0fdba 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -4,21 +4,36 @@ package highlight import ( + "html/template" "strings" "testing" "github.com/stretchr/testify/assert" ) -func lines(s string) []string { - return strings.Split(strings.ReplaceAll(strings.TrimSpace(s), `\n`, "\n"), "\n") +func lines(s string) (out []template.HTML) { + // "" => [], "a" => ["a"], "a\n" => ["a\n"], "a\nb" => ["a\n", "b"] (each line always includes EOL "\n" if it exists) + out = make([]template.HTML, 0) + s = strings.ReplaceAll(strings.ReplaceAll(strings.TrimSpace(s), "\n", ""), `\n`, "\n") + for { + if p := strings.IndexByte(s, '\n'); p != -1 { + out = append(out, template.HTML(s[:p+1])) + s = s[p+1:] + } else { + break + } + } + if s != "" { + out = append(out, template.HTML(s)) + } + return out } func TestFile(t *testing.T) { tests := []struct { name string code string - want []string + want []template.HTML lexerName string }{ { @@ -99,10 +114,7 @@ c=2 t.Run(tt.name, func(t *testing.T) { out, lexerName, err := File(tt.name, "", []byte(tt.code)) assert.NoError(t, err) - expected := strings.Join(tt.want, "\n") - actual := strings.Join(out, "\n") - assert.Equal(t, strings.Count(actual, "")) - assert.EqualValues(t, expected, actual) + assert.EqualValues(t, tt.want, out) assert.Equal(t, tt.lexerName, lexerName) }) } @@ -112,7 +124,7 @@ func TestPlainText(t *testing.T) { tests := []struct { name string code string - want []string + want []template.HTML }{ { name: "empty.py", @@ -165,9 +177,7 @@ c=2`), for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { out := PlainText([]byte(tt.code)) - expected := strings.Join(tt.want, "\n") - actual := strings.Join(out, "\n") - assert.EqualValues(t, expected, actual) + assert.EqualValues(t, tt.want, out) }) } } diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index fdb468df1ab5b..e19e22eea0e1e 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -6,6 +6,7 @@ package code import ( "bytes" "context" + "html/template" "strings" "code.gitea.io/gitea/modules/highlight" @@ -22,7 +23,7 @@ type Result struct { Language string Color string LineNumbers []int - FormattedLines string + FormattedLines template.HTML } type SearchResultLanguages = internal.SearchResultLanguages diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index c1e0144199323..e7af02b49670f 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -87,7 +87,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error } lexer = chroma.Coalesce(lexer) - if _, err := w.WriteString(highlight.CodeFromLexer(lexer, source)); err != nil { + if _, err := w.WriteString(string(highlight.CodeFromLexer(lexer, source))); err != nil { return "" } } diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 31042d3ee0dda..f94e6206cd995 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -35,6 +35,8 @@ var UI = struct { OnlyShowRelevantRepos bool ExploreDefaultSort string `ini:"EXPLORE_PAGING_DEFAULT_SORT"` + AmbiguousUnicodeDetection bool + Notification struct { MinTimeout time.Duration TimeoutStep time.Duration @@ -82,6 +84,9 @@ var UI = struct { Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, + + AmbiguousUnicodeDetection: true, + Notification: struct { MinTimeout time.Duration TimeoutStep time.Duration diff --git a/modules/util/string.go b/modules/util/string.go index f2def7b0ece2c..2cf44d29b1f0a 100644 --- a/modules/util/string.go +++ b/modules/util/string.go @@ -3,7 +3,7 @@ package util -import "github.com/yuin/goldmark/util" +import "unsafe" func isSnakeCaseUpper(c byte) bool { return 'A' <= c && c <= 'Z' @@ -83,5 +83,15 @@ func ToSnakeCase(input string) string { } } } - return util.BytesToReadOnlyString(res) + return UnsafeBytesToString(res) +} + +// UnsafeBytesToString uses Go's unsafe package to convert a byte slice to a string. +// TODO: replace all "goldmark/util.BytesToReadOnlyString" with this official approach +func UnsafeBytesToString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} + +func UnsafeStringToBytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) } diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index db9be51257e7f..b2374e32c2849 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -315,8 +315,7 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m lexerName = lexerNameForLine } - br.EscapeStatus, line = charset.EscapeControlHTML(line, ctx.Locale) - br.Code = gotemplate.HTML(line) + br.EscapeStatus, br.Code = charset.EscapeControlHTML(line, ctx.Locale) rows = append(rows, br) escapeStatus = escapeStatus.Or(br.EscapeStatus) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 70556185bb53b..9cf0dff5d8e2f 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -9,6 +9,7 @@ import ( gocontext "context" "encoding/base64" "fmt" + "html/template" "image" "io" "net/http" @@ -317,19 +318,18 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr }, rd) if err != nil { log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err) - buf := &bytes.Buffer{} - ctx.Data["EscapeStatus"], _ = charset.EscapeControlStringReader(rd, buf, ctx.Locale) - ctx.Data["FileContent"] = buf.String() + delete(ctx.Data, "IsMarkup") } - } else { + } + + if ctx.Data["IsMarkup"] != true { ctx.Data["IsPlainText"] = true - buf := &bytes.Buffer{} - ctx.Data["EscapeStatus"], err = charset.EscapeControlStringReader(rd, buf, ctx.Locale) + content, err := io.ReadAll(rd) if err != nil { - log.Error("Read failed: %v", err) + log.Error("Read readme content failed: %v", err) } - - ctx.Data["FileContent"] = buf.String() + contentEscaped := template.HTMLEscapeString(util.UnsafeBytesToString(content)) + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale) } if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { @@ -493,7 +493,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st buf, _ := io.ReadAll(rd) // The Open Group Base Specification: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html - // empty: 0 lines; "a": 1 line, 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line; + // empty: 0 lines; "a": 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line; // Gitea uses the definition (like most modern editors): // empty: 0 lines; "a": 1 line; "a\n": 2 lines; "a\nb": 2 lines; // When rendering, the last empty line is not rendered in UI, while the line-number is still counted, to tell users that the file contains a trailing EOL. @@ -620,7 +620,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st } } -func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input io.Reader) (escaped *charset.EscapeStatus, output string, err error) { +func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input io.Reader) (escaped *charset.EscapeStatus, output template.HTML, err error) { markupRd, markupWr := io.Pipe() defer markupWr.Close() done := make(chan struct{}) @@ -628,7 +628,7 @@ func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input i sb := &strings.Builder{} // We allow NBSP here this is rendered escaped, _ = charset.EscapeControlReader(markupRd, sb, ctx.Locale, charset.RuneNBSP) - output = sb.String() + output = template.HTML(sb.String()) close(done) }() err = markup.Render(renderCtx, input, markupWr) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 75fc687c86a52..0f6e2b6c17d5b 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -285,15 +285,15 @@ type DiffInline struct { // DiffInlineWithUnicodeEscape makes a DiffInline with hidden unicode characters escaped func DiffInlineWithUnicodeEscape(s template.HTML, locale translation.Locale) DiffInline { - status, content := charset.EscapeControlHTML(string(s), locale) - return DiffInline{EscapeStatus: status, Content: template.HTML(content)} + status, content := charset.EscapeControlHTML(s, locale) + return DiffInline{EscapeStatus: status, Content: content} } // DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped func DiffInlineWithHighlightCode(fileName, language, code string, locale translation.Locale) DiffInline { highlighted, _ := highlight.Code(fileName, language, code) status, content := charset.EscapeControlHTML(highlighted, locale) - return DiffInline{EscapeStatus: status, Content: template.HTML(content)} + return DiffInline{EscapeStatus: status, Content: content} } // GetComputedInlineDiffFor computes inline diff for the given line. diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go index f1e2b1d3cb31a..35d48445504ae 100644 --- a/services/gitdiff/highlightdiff.go +++ b/services/gitdiff/highlightdiff.go @@ -93,10 +93,10 @@ func (hcd *highlightCodeDiff) diffWithHighlight(filename, language, codeA, codeB highlightCodeA, _ := highlight.Code(filename, language, codeA) highlightCodeB, _ := highlight.Code(filename, language, codeB) - highlightCodeA = hcd.convertToPlaceholders(highlightCodeA) - highlightCodeB = hcd.convertToPlaceholders(highlightCodeB) + convertedCodeA := hcd.convertToPlaceholders(string(highlightCodeA)) + convertedCodeB := hcd.convertToPlaceholders(string(highlightCodeB)) - diffs := diffMatchPatch.DiffMain(highlightCodeA, highlightCodeB, true) + diffs := diffMatchPatch.DiffMain(convertedCodeA, convertedCodeB, true) diffs = diffMatchPatch.DiffCleanupEfficiency(diffs) for i := range diffs { diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 4129a133b7e76..e7d1c04c12ef2 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -74,9 +74,9 @@ {{end}}
{{if .IsMarkup}} - {{if .FileContent}}{{.FileContent | Safe}}{{end}} + {{if .FileContent}}{{.FileContent}}{{end}} {{else if .IsPlainText}} -
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
+
{{if .FileContent}}{{.FileContent}}{{end}}
{{else if not .IsTextSource}}
{{if .IsImageFile}} @@ -114,7 +114,7 @@ {{if $.EscapeStatus.Escaped}} {{if (index $.LineEscapeStatus $idx).Escaped}}{{end}} {{end}} - {{$code | Safe}} + {{$code}} {{end}} From 69f2b698c5000a566bc04267f7e9bd387b880109 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 18 Dec 2023 00:25:45 +0000 Subject: [PATCH 6/6] [skip ci] Updated translations via Crowdin --- options/locale/locale_ja-JP.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 7a7dcea8f8988..c2557b180e7ce 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -888,7 +888,7 @@ webauthn_nickname=ニックネーム webauthn_delete_key=セキュリティキーの登録解除 webauthn_delete_key_desc=セキュリティキーの登録を解除すると、今後そのセキュリティキーでサインインすることはできなくなります。 続行しますか? webauthn_key_loss_warning=セキュリティキーを紛失すると、アカウントへのアクセスを失います。 -webauthn_alternative_tip=もうひとつ別の認証方法も設定すると良いかもしれません。 +webauthn_alternative_tip=もうひとつ別の認証方法も設定しておくと良いでしょう。 manage_account_links=連携アカウントの管理 manage_account_links_desc=これらの外部アカウントがGiteaアカウントと連携されています。 @@ -3457,7 +3457,7 @@ owner.settings.chef.keypair.description=Chefレジストリの認証にはキー [secrets] secrets=シークレット description=シークレットは特定のActionsに渡されます。 それ以外で読み出されることはありません。 -none=まだシークレットはありません。 +none=シークレットはまだありません。 creation=シークレットを追加 creation.name_placeholder=大文字小文字の区別なし、英数字とアンダースコアのみ、GITEA_ や GITHUB_ で始まるものは不可 creation.value_placeholder=内容を入力してください。前後の空白は除去されます。