Compare commits

...

7 Commits

Author SHA1 Message Date
silverwind 3746a625f5
Tweak repo buttons on mobile and labeled button border-radius (#30503)
Fixes: https://github.com/go-gitea/gitea/issues/30514
Fixes:
https://github.com/go-gitea/gitea/pull/30288#issuecomment-2057466623

- Fix border-radius regression from
https://github.com/go-gitea/gitea/pull/30475
- Fix and simplify hover state
- Move the modal HTML so it does not interfere with the CSS
- Make the star and unwatch text show on mobile. There is still plenty
of space, below is iPhone 12 viewport size

<img width="696" alt="Screenshot 2024-04-15 at 20 34 03"
src="https://github.com/go-gitea/gitea/assets/115237/af90bb00-4671-4973-a255-8eb44ee6ba8d">
<img width="230" alt="Screenshot 2024-04-15 at 20 31 42"
src="https://github.com/go-gitea/gitea/assets/115237/986ef533-7a01-4bb0-8dcd-fd19e4259e84">
<img width="233" alt="Screenshot 2024-04-15 at 20 31 47"
src="https://github.com/go-gitea/gitea/assets/115237/5b825dd8-0ccc-4d56-9d8f-774abb935b68">

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-16 17:46:12 +02:00
silverwind a658e2f277
Fix long branch name overflows (#30345)
Fixes: https://github.com/go-gitea/gitea/issues/27971
Fixes: https://github.com/go-gitea/gitea/pull/28010

<img width="689" alt="Screenshot 2024-04-09 at 00 19 57"
src="https://github.com/go-gitea/gitea/assets/115237/7c895a47-274f-40a6-a126-290658f1982d">

Also fixes a similar issue in issue list where CSS was there but not
active because of missing `display: block`.

<img width="372" alt="Screenshot 2024-04-09 at 00 18 25"
src="https://github.com/go-gitea/gitea/assets/115237/cfbee7cd-2e15-4ac7-96ce-020816f48798">
2024-04-16 08:52:45 +00:00
Tobias Balle-Petersen 58b204b813
Update API to return 'source_id' for users (#29718)
Using the API, a user's _source_id_ can be set in the _CreateUserOption_
model, but the field is not returned in the _User_ model.

This PR updates the _User_ model to include the field _source_id_ (The
ID of the Authentication Source).
2024-04-16 06:08:48 +00:00
SimonErm 6ba0c371c2
Allow `preferred_username` as username source for OIDC (#30454)
This PR adds the preferred_username claim as a possible username source
for the oauth2_client.

Closes #21518
2024-04-16 05:41:39 +00:00
yp05327 cf9061f44a
Fix empty field `login_name` in API response JSON when creating user (#30511)
Fix #30508

ps: if `sourceID` is not set, `LoginName` will be ignored
2024-04-16 13:13:00 +08:00
Bo-Yi Wu c70e442ce4
feat(api): implement branch/commit comparison API (#30349)
- Add new `Compare` struct to represent comparison between two commits
- Introduce new API endpoint `/compare/*` to get commit comparison
information
- Create new file `repo_compare.go` with the `Compare` struct definition
- Add new file `compare.go` in `routers/api/v1/repo` to handle
comparison logic
- Add new file `compare.go` in `routers/common` to define `CompareInfo`
struct
- Refactor `ParseCompareInfo` function to use `common.CompareInfo`
struct
- Update Swagger documentation to include the new API endpoint for
commit comparison
- Remove duplicate `CompareInfo` struct from
`routers/web/repo/compare.go`
- Adjust base path in Swagger template to be relative (`/api/v1`)

GitHub API
https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits

---------

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-16 11:45:04 +08:00
GiteaBot 3b045ee165 [skip ci] Updated translations via Crowdin 2024-04-16 00:23:51 +00:00
30 changed files with 366 additions and 77 deletions

View File

@ -1553,8 +1553,9 @@ LEVEL = Info
;; The source of the username for new oauth2 accounts:
;; userid = use the userid / sub attribute
;; nickname = use the nickname attribute
;; preferred_username = use the preferred_username attribute
;; email = use the username part of the email attribute
;; Note: `nickname` and `email` options will normalize input strings using the following criteria:
;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
;; - diacritics are removed
;; - the characters in the set `['´\x60]` are removed
;; - the characters in the set `[\s~+]` are replaced with `-`

View File

@ -608,9 +608,10 @@ And the following unique queues:
- `ENABLE_AUTO_REGISTRATION`: **false**: Automatically create user accounts for new oauth2 users.
- `USERNAME`: **nickname**: The source of the username for new oauth2 accounts:
- `userid` - use the userid / sub attribute
- `nickname` - use the nickname attribute
- `nickname` - use the nickname
- `preferred_username` - use the preferred_username
- `email` - use the username part of the email attribute
- Note: `nickname` and `email` options will normalize input strings using the following criteria:
- Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
- diacritics are removed
- the characters in the set `['´\x60]` are removed
- the characters in the set `[\s~+]` are replaced with `-`

View File

@ -22,11 +22,13 @@ const (
OAuth2UsernameNickname OAuth2UsernameType = "nickname"
// OAuth2UsernameEmail username of oauth2 email field will be used as gitea name
OAuth2UsernameEmail OAuth2UsernameType = "email"
// OAuth2UsernameEmail username of oauth2 preferred_username field will be used as gitea name
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username"
)
func (username OAuth2UsernameType) isValid() bool {
switch username {
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail:
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail, OAuth2UsernamePreferredUsername:
return true
}
return false

View File

@ -0,0 +1,10 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package structs
// Compare represents a comparison between two commits.
type Compare struct {
TotalCommits int `json:"total_commits"` // Total number of commits in the comparison.
Commits []*Commit `json:"commits"` // List of commits in the comparison.
}

View File

@ -20,6 +20,8 @@ type User struct {
// the user's authentication sign-in name.
// default: empty
LoginName string `json:"login_name"`
// The ID of the user's Authentication Source
SourceID int64 `json:"source_id"`
// the user's full name
FullName string `json:"full_name"`
// swagger:strfmt email

View File

@ -25,6 +25,7 @@ enable_javascript=このウェブサイトにはJavaScriptが必要です。
toc=目次
licenses=ライセンス
return_to_gitea=Giteaに戻る
more_items=その他の項目
username=ユーザー名
email=メールアドレス
@ -1003,6 +1004,7 @@ fork_visibility_helper=フォークしたリポジトリの公開/非公開は
fork_branch=フォークにクローンされるブランチ
all_branches=すべてのブランチ
fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。
fork.blocked_user=リポジトリのオーナーがあなたをブロックしているため、リポジトリをフォークできません。
use_template=このテンプレートを使用
open_with_editor=%s で開く
download_zip=ZIPファイルをダウンロード
@ -1179,6 +1181,7 @@ watch=ウォッチ
unstar=スター取消
star=スター
fork=フォーク
action.blocked_user=リポジトリのオーナーがあなたをブロックしているため、アクションを実行できません。
download_archive=リポジトリをダウンロード
more_operations=その他の操作
@ -1427,6 +1430,8 @@ issues.new.assignees=担当者
issues.new.clear_assignees=担当者をクリア
issues.new.no_assignees=担当者なし
issues.new.no_reviewers=レビューアなし
issues.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、イシューを作成できません。
issues.edit.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、内容を編集できません。
issues.choose.get_started=始める
issues.choose.open_external_link=オープン
issues.choose.blank=デフォルト
@ -1541,6 +1546,7 @@ issues.close_comment_issue=コメントしてクローズ
issues.reopen_issue=再オープンする
issues.reopen_comment_issue=コメントして再オープン
issues.create_comment=コメントする
issues.comment.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、コメントの作成や編集はできません。
issues.closed_at=`がイシューをクローズ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`がイシューを再オープン <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`がコミットでこのイシューを参照 <a id="%[1]s" href="#%[1]s">%[2]s</a>`
@ -1739,6 +1745,7 @@ compare.compare_head=比較
pulls.desc=プルリクエストとコードレビューの有効化。
pulls.new=新しいプルリクエスト
pulls.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、プルリクエストを作成できません。
pulls.view=プルリクエストを表示
pulls.compare_changes=新規プルリクエスト
pulls.allow_edits_from_maintainers=メンテナーからの編集を許可する
@ -1960,6 +1967,7 @@ wiki.original_git_entry_tooltip=フレンドリーリンクを使用する代わ
activity=アクティビティ
activity.navbar.pulse=Pulse
activity.navbar.code_frequency=コード更新頻度
activity.navbar.contributors=貢献者
activity.navbar.recent_commits=最近のコミット
activity.period.filter_label=期間:
@ -2080,6 +2088,8 @@ settings.branches.add_new_rule=新しいルールを追加
settings.advanced_settings=拡張設定
settings.wiki_desc=Wikiを有効にする
settings.use_internal_wiki=ビルトインのWikiを使用する
settings.default_wiki_branch_name=デフォルトのWikiブランチ名
settings.failed_to_change_default_wiki_branch=デフォルトのWikiブランチを変更できませんでした。
settings.use_external_wiki=外部のWikiを使用する
settings.external_wiki_url=外部WikiのURL
settings.external_wiki_url_error=外部WikiのURLが有効なURLではありません。
@ -2110,6 +2120,9 @@ settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテ
settings.releases_desc=リリースを有効にする
settings.packages_desc=リポジトリパッケージレジストリを有効にする
settings.projects_desc=プロジェクトを有効にする
settings.projects_mode_desc=プロジェクト モード (表示するプロジェクトの種類)
settings.projects_mode_repo=リポジトリのプロジェクトのみ
settings.projects_mode_owner=ユーザーや組織のプロジェクトのみ
settings.projects_mode_all=すべてのプロジェクト
settings.actions_desc=Actionsを有効にする
settings.admin_settings=管理者用設定
@ -2136,6 +2149,7 @@ settings.convert_fork_succeed=フォークを通常のリポジトリに変換
settings.transfer=オーナー移転
settings.transfer.rejected=リポジトリの移転は拒否されました。
settings.transfer.success=リポジトリの移転が成功しました。
settings.transfer.blocked_user=新しいオーナーがあなたをブロックしているため、リポジトリを移転できません。
settings.transfer_abort=転送をキャンセル
settings.transfer_abort_invalid=存在しないリポジトリの移転はキャンセルできません。
settings.transfer_abort_success=%s へのリポジトリ移転は正常にキャンセルされました。
@ -2181,6 +2195,7 @@ settings.add_collaborator_success=共同作業者を追加しました。
settings.add_collaborator_inactive_user=アクティベートされていないユーザーを共同作業者として追加することはできません。
settings.add_collaborator_owner=共同作業者としてオーナーを追加することはできません。
settings.add_collaborator_duplicate=共同作業者として既にこのリポジトリに追加されています。
settings.add_collaborator.blocked_user=共同作業者がリポジトリのオーナーによってブロックされているか、またはブロックしています。
settings.delete_collaborator=削除
settings.collaborator_deletion=共同作業者の削除
settings.collaborator_deletion_desc=共同作業者を削除し、このリポジトリへのアクセス権を取り消します。 続行しますか?
@ -2619,12 +2634,14 @@ find_file.no_matching=一致するファイルが見つかりません
error.csv.too_large=このファイルは大きすぎるため表示できません。
error.csv.unexpected=このファイルは %d 行目の %d 文字目に予期しない文字が含まれているため表示できません。
error.csv.invalid_field_count=このファイルは %d 行目のフィールドの数が正しくないため表示できません。
error.broken_git_hook=このリポジトリのGitフックが壊れているようです。 <a target="_blank" rel="noreferrer" href="%s">ドキュメント</a>に従って修正し、その後いくつかのコミットをプッシュして状態を最新にしてください。
[graphs]
component_loading=%sを読み込み中...
component_loading_failed=%sを読み込めませんでした
component_loading_info=少し時間がかかるかもしれません…
component_failed_to_load=予期しないエラーが発生しました。
code_frequency.what=コード更新頻度
contributors.what=実績
recent_commits.what=最近のコミット
@ -2753,6 +2770,7 @@ teams.invite.by=%s からの招待
teams.invite.description=下のボタンをクリックしてチームに参加してください。
[admin]
maintenance=メンテナンス
dashboard=ダッシュボード
self_check=セルフチェック
identity_access=アイデンティティとアクセス
@ -2776,6 +2794,7 @@ settings=管理設定
dashboard.new_version_hint=Gitea %s が入手可能になりました。 現在実行しているのは %s です。 詳細は <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">ブログ</a> を確認してください。
dashboard.statistic=サマリー
dashboard.maintenance_operations=メンテナンス操作
dashboard.system_status=システム状況
dashboard.operation_name=操作の名称
dashboard.operation_switch=切り替え
@ -3282,6 +3301,7 @@ notices.op=操作
notices.delete_success=システム通知を削除しました。
self_check.no_problem_found=今のところ問題は見つかっていません。
self_check.startup_warnings=起動時の警告:
self_check.database_collation_mismatch=データベースに想定される照合順序: %s
self_check.database_collation_case_insensitive=データベースは照合順序 %s を使用しており、大文字小文字を区別しません。 Giteaはその照合順序でも動作するかもしれませんが、まれに期待どおり動作しないケースがあるかもしれません。
self_check.database_inconsistent_collation_columns=データベースは照合順序 %s を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。

View File

@ -30,7 +30,7 @@ import (
user_service "code.gitea.io/gitea/services/user"
)
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) {
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64) {
if sourceID == 0 {
return
}
@ -47,7 +47,6 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64
u.LoginType = source.Type
u.LoginSource = source.ID
u.LoginName = loginName
}
// CreateUser create a user
@ -83,12 +82,13 @@ func CreateUser(ctx *context.APIContext) {
Passwd: form.Password,
MustChangePassword: true,
LoginType: auth.Plain,
LoginName: form.LoginName,
}
if form.MustChangePassword != nil {
u.MustChangePassword = *form.MustChangePassword
}
parseAuthSource(ctx, u, form.SourceID, form.LoginName)
parseAuthSource(ctx, u, form.SourceID)
if ctx.Written() {
return
}

View File

@ -1066,6 +1066,8 @@ func Routes() *web.Route {
m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate)
m.Group("/{username}/{reponame}", func() {
m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff)
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
Delete(reqToken(), reqOwner(), repo.Delete).
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)

View File

@ -0,0 +1,99 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"net/http"
"strings"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/gitrepo"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
// CompareDiff compare two branches or commits
func CompareDiff(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} Get commit comparison information
// ---
// summary: Get commit comparison information
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: basehead
// in: path
// description: compare two branches or commits
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/Compare"
// "404":
// "$ref": "#/responses/notFound"
if ctx.Repo.GitRepo == nil {
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return
}
ctx.Repo.GitRepo = gitRepo
defer gitRepo.Close()
}
infoPath := ctx.Params("*")
infos := []string{ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository.DefaultBranch}
if infoPath != "" {
infos = strings.SplitN(infoPath, "...", 2)
if len(infos) != 2 {
if infos = strings.SplitN(infoPath, "..", 2); len(infos) != 2 {
infos = []string{ctx.Repo.Repository.DefaultBranch, infoPath}
}
}
}
_, _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{
Base: infos[0],
Head: infos[1],
})
if ctx.Written() {
return
}
defer headGitRepo.Close()
verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
files := ctx.FormString("files") == "" || ctx.FormBool("files")
apiCommits := make([]*api.Commit, 0, len(ci.Commits))
userCache := make(map[string]*user_model.User)
for i := 0; i < len(ci.Commits); i++ {
apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache,
convert.ToCommitOptions{
Stat: true,
Verification: verification,
Files: files,
})
if err != nil {
ctx.ServerError("toCommit", err)
return
}
apiCommits = append(apiCommits, apiCommit)
}
ctx.JSON(http.StatusOK, &api.Compare{
TotalCommits: len(ci.Commits),
Commits: apiCommits,
})
}

View File

@ -414,3 +414,9 @@ type swaggerRepoNewIssuePinsAllowed struct {
// in:body
Body api.NewIssuePinsAllowed `json:"body"`
}
// swagger:response Compare
type swaggerCompare struct {
// in:body
Body api.Compare `json:"body"`
}

21
routers/common/compare.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
)
// CompareInfo represents the collected results from ParseCompareInfo
type CompareInfo struct {
HeadUser *user_model.User
HeadRepo *repo_model.Repository
HeadGitRepo *git.Repository
CompareInfo *git.CompareInfo
BaseBranch string
HeadBranch string
DirectComparison bool
}

View File

@ -386,6 +386,13 @@ func getUserName(gothUser *goth.User) (string, error) {
switch setting.OAuth2Client.Username {
case setting.OAuth2UsernameEmail:
return user_model.NormalizeUserName(strings.Split(gothUser.Email, "@")[0])
case setting.OAuth2UsernamePreferredUsername:
preferredUsername, exists := gothUser.RawData["preferred_username"]
if exists {
return user_model.NormalizeUserName(preferredUsername.(string))
} else {
return "", fmt.Errorf("preferred_username is missing in received user data but configured as username source for user_id %q. Check if OPENID_CONNECT_SCOPES contains profile", gothUser.UserID)
}
case setting.OAuth2UsernameNickname:
return user_model.NormalizeUserName(gothUser.NickName)
default: // OAuth2UsernameUserid

View File

@ -35,6 +35,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/gitdiff"
@ -185,21 +186,10 @@ func setCsvCompareContext(ctx *context.Context) {
}
}
// CompareInfo represents the collected results from ParseCompareInfo
type CompareInfo struct {
HeadUser *user_model.User
HeadRepo *repo_model.Repository
HeadGitRepo *git.Repository
CompareInfo *git.CompareInfo
BaseBranch string
HeadBranch string
DirectComparison bool
}
// ParseCompareInfo parse compare info between two commit for preparing comparing references
func ParseCompareInfo(ctx *context.Context) *CompareInfo {
func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
baseRepo := ctx.Repo.Repository
ci := &CompareInfo{}
ci := &common.CompareInfo{}
fileOnly := ctx.FormBool("file-only")
@ -576,7 +566,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
// PrepareCompareDiff renders compare diff page
func PrepareCompareDiff(
ctx *context.Context,
ci *CompareInfo,
ci *common.CompareInfo,
whitespaceBehavior git.TrustedCmdArgs,
) bool {
var (

View File

@ -75,6 +75,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
if authed {
result.IsAdmin = user.IsAdmin
result.LoginName = user.LoginName
result.SourceID = user.LoginSource
result.LastLogin = user.LastLoginUnix.AsTime()
result.Language = user.Language
result.IsActive = user.IsActive

View File

@ -71,7 +71,7 @@
{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
<div class="ui dropdown custom">
<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0">
<span class="text tw-flex tw-items-center tw-mr-1">
<span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
{{if .release}}
{{ctx.Locale.Tr "repo.release.compare"}}
{{else}}
@ -80,7 +80,7 @@
{{else}}
{{svg "octicon-git-branch"}}
{{end}}
<strong ref="dropdownRefName" class="tw-ml-2">{{if and .root.IsViewTag (not .noTag)}}{{.root.TagName}}{{else if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.CommitID}}{{end}}</strong>
<strong ref="dropdownRefName" class="tw-ml-2 tw-inline-block gt-ellipsis">{{if and .root.IsViewTag (not .noTag)}}{{.root.TagName}}{{else if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.CommitID}}{{end}}</strong>
{{end}}
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}

View File

@ -91,28 +91,28 @@
>
{{svg "octicon-repo-forked"}}<span class="text not-mobile">{{ctx.Locale.Tr "repo.fork"}}</span>
</a>
<div class="ui small modal" id="fork-repo-modal">
<div class="header">
{{ctx.Locale.Tr "repo.already_forked" .Name}}
</div>
<div class="content tw-text-left">
<div class="ui list">
{{range $.UserAndOrgForks}}
<div class="ui item tw-py-2">
<a href="{{.Link}}">{{svg "octicon-repo-forked" 16 "tw-mr-2"}}{{.FullName}}</a>
</div>
{{end}}
</div>
{{if $.CanSignedUserFork}}
<div class="divider"></div>
<a href="{{$.RepoLink}}/fork">{{ctx.Locale.Tr "repo.fork_to_different_account"}}</a>
{{end}}
</div>
</div>
<a class="ui basic label" href="{{.Link}}/forks">
{{CountFmt .NumForks}}
</a>
</div>
<div class="ui small modal" id="fork-repo-modal">
<div class="header">
{{ctx.Locale.Tr "repo.already_forked" .Name}}
</div>
<div class="content tw-text-left">
<div class="ui list">
{{range $.UserAndOrgForks}}
<div class="ui item tw-py-2">
<a href="{{.Link}}">{{svg "octicon-repo-forked" 16 "tw-mr-2"}}{{.FullName}}</a>
</div>
{{end}}
</div>
{{if $.CanSignedUserFork}}
<div class="divider"></div>
<a href="{{$.RepoLink}}/fork">{{ctx.Locale.Tr "repo.fork_to_different_account"}}</a>
{{end}}
</div>
</div>
{{end}}
</div>
{{end}}

View File

@ -5,9 +5,9 @@
{{$.CsrfTokenHtml}}
</form>
{{/* TODO: share this branch selector dropdown with the same in repo page */}}
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown" data-no-results="{{ctx.Locale.Tr "no_results_found"}}">
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown tw-max-w-full" data-no-results="{{ctx.Locale.Tr "no_results_found"}}">
<div class="ui basic small button">
<span class="text branch-name">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
<span class="text branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
{{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}
</div>
<div class="menu">
@ -37,7 +37,7 @@
<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
{{end}}
{{range .Branches}}
<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector" title="{{.}}">{{.}}</div>
{{else}}
<div class="item">{{ctx.Locale.Tr "no_results_found"}}</div>
{{end}}

View File

@ -41,7 +41,7 @@
{{else}}
<div class="ui green label issue-state-label">{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues.open_title"}}</div>
{{end}}
<div class="tw-ml-2">
<div class="tw-ml-2 tw-flex-1 tw-break-anywhere">
{{if .Issue.IsPull}}
{{$headHref := .HeadTarget}}
{{if .HeadBranchLink}}

View File

@ -4,7 +4,7 @@
{{if $.IsStaringRepo}}{{$buttonText = ctx.Locale.Tr "repo.unstar"}}{{end}}
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
{{if $.IsStaringRepo}}{{svg "octicon-star-fill"}}{{else}}{{svg "octicon-star"}}{{end}}
<span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
<span aria-hidden="true">{{$buttonText}}</span>
</button>
<a hx-boost="false" class="ui basic label" href="{{$.RepoLink}}/stars">
{{CountFmt .Repository.NumStars}}

View File

@ -4,7 +4,7 @@
{{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}}
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
{{svg "octicon-eye"}}
<span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
<span aria-hidden="true">{{$buttonText}}</span>
</button>
<a hx-boost="false" class="ui basic label" href="{{.RepoLink}}/watchers">
{{CountFmt .Repository.NumWatches}}

View File

@ -88,18 +88,21 @@
</div>
{{end}}
{{if and .Milestone (ne $.listType "milestone")}}
<a class="milestone flex-text-inline" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
{{svg "octicon-milestone" 14}}{{.Milestone.Name}}
<a class="milestone flex-text-inline tw-max-w-[300px]" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
{{svg "octicon-milestone" 14}}
<span class="gt-ellipsis">{{.Milestone.Name}}</span>
</a>
{{end}}
{{if .Project}}
<a class="project flex-text-inline" href="{{.Project.Link ctx}}">
{{svg .Project.IconName 14}}{{.Project.Title}}
<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Project.Link ctx}}">
{{svg .Project.IconName 14}}
<span class="gt-ellipsis">{{.Project.Title}}</span>
</a>
{{end}}
{{if .Ref}}
<a class="ref flex-text-inline" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
{{svg "octicon-git-branch" 14}}{{index $.IssueRefEndNames .ID}}
<a class="ref flex-text-inline tw-max-w-[300px]" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
{{svg "octicon-git-branch" 14}}
<span class="gt-ellipsis">{{index $.IssueRefEndNames .ID}}</span>
</a>
{{end}}
{{$tasks := .GetTasks}}

View File

@ -5340,6 +5340,51 @@
}
}
},
"/repos/{owner}/{repo}/compare/{basehead}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Get",
"commit",
"comparison"
],
"summary": "Get commit comparison information",
"operationId": "information",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "compare two branches or commits",
"name": "basehead",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/Compare"
},
"404": {
"$ref": "#/responses/notFound"
}
}
}
},
"/repos/{owner}/{repo}/contents": {
"get": {
"produces": [
@ -18717,6 +18762,25 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"Compare": {
"type": "object",
"title": "Compare represents a comparison between two commits.",
"properties": {
"commits": {
"type": "array",
"items": {
"$ref": "#/definitions/Commit"
},
"x-go-name": "Commits"
},
"total_commits": {
"type": "integer",
"format": "int64",
"x-go-name": "TotalCommits"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"ContentsResponse": {
"description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content",
"type": "object",
@ -24196,6 +24260,12 @@
"type": "boolean",
"x-go-name": "Restricted"
},
"source_id": {
"description": "The ID of the user's Authentication Source",
"type": "integer",
"format": "int64",
"x-go-name": "SourceID"
},
"starred_repos_count": {
"type": "integer",
"format": "int64",
@ -24678,6 +24748,12 @@
}
}
},
"Compare": {
"description": "",
"schema": {
"$ref": "#/definitions/Compare"
}
},
"ContentsListResponse": {
"description": "ContentsListResponse",
"schema": {

View File

@ -0,0 +1,38 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"net/http"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestAPICompareBranches(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Login as User2.
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
repoName := "repo20"
req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/add-csv...remove-files-b", repoName).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
var apiResp *api.Compare
DecodeJSON(t, resp, &apiResp)
assert.Equal(t, 2, apiResp.TotalCommits)
assert.Len(t, apiResp.Commits, 2)
}

View File

@ -342,6 +342,8 @@ a.label,
.ui.dropdown .menu > .item {
color: var(--color-text);
overflow: hidden;
text-overflow: ellipsis;
}
.ui.dropdown .menu > .item:hover {

View File

@ -63,6 +63,8 @@
}
.ui.labeled.button > .button {
margin: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.ui.labeled.button > .label {
display: flex;
@ -70,6 +72,14 @@
margin: 0 0 0 -1px !important;
font-size: 1em;
border-color: var(--color-light-border);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.ui.labeled.button > .label:hover {
background: var(--color-hover);
}
.ui.labeled.button > .button:hover + .label {
border-left-color: var(--color-secondary-dark-2);
}
.ui.button > .icon:not(.button) {

View File

@ -107,7 +107,7 @@ a.ui.label:hover {
a.ui.basic.label:hover {
text-decoration: none;
color: var(--color-text);
border-color: var(--color-light-border);
border-color: var(--color-secondary-dark-2);
background: var(--color-hover);
}

View File

@ -50,6 +50,11 @@
width: 300px;
}
.issue-content-right .dropdown > .menu {
max-width: 270px;
min-width: 0;
}
@media (max-width: 767.98px) {
.issue-content-left,
.issue-content-right {
@ -57,11 +62,6 @@
}
}
.repository .issue-content-right .menu {
overflow-x: auto;
max-height: 500px;
}
.repository .issue-content-right .ui.list .dependency {
padding: 0;
white-space: nowrap;
@ -113,11 +113,6 @@
left: 0;
}
.repository .filter.menu .ui.dropdown .menu .item {
overflow: hidden;
text-overflow: ellipsis;
}
.repository .select-label .desc {
padding-left: 23px;
}
@ -672,6 +667,7 @@ td .commit-summary {
font-size: 14px !important;
padding: 7px 10px !important;
border-radius: var(--border-radius-medium) !important;
flex-shrink: 0;
}
.issue-state-label .svg {
@ -1170,10 +1166,6 @@ td .commit-summary {
font-size: 14px;
}
.repository .ui.dropdown.filter > .menu {
margin-top: 1px;
}
.repository.branches .commit-divergence .bar-group {
position: relative;
float: left;

View File

@ -36,11 +36,6 @@
gap: 0.25em;
}
.repo-buttons .ui.labeled.button > .label:hover {
color: var(--color-primary-light-2);
background: var(--color-light);
}
.repo-buttons button[disabled] ~ .label {
opacity: var(--opacity-disabled);
color: var(--color-text-dark);
@ -67,3 +62,12 @@
.repo-buttons .ui.labeled.button.disabled > .button {
pointer-events: none !important;
}
@media (max-width: 767.98px) {
.repo-buttons .ui.button,
.repo-buttons .ui.label {
padding-left: 8px;
padding-right: 8px;
margin: 0;
}
}

View File

@ -39,7 +39,7 @@
}
#issue-list .flex-item-body .branches .branch {
background-color: var(--color-secondary-alpha-40);
background-color: var(--color-secondary-alpha-50);
border-radius: var(--border-radius);
padding: 0 4px;
}
@ -48,7 +48,9 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 10em;
max-width: 200px;
display: inline-block;
vertical-align: top;
}
#issue-list .flex-item-body .checklist progress {

View File

@ -248,12 +248,12 @@ export default sfc; // activate IDE's Vue plugin
<template>
<div class="ui dropdown custom">
<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
<span class="text tw-flex tw-items-center tw-mr-1">
<span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
<template v-if="release">{{ textReleaseCompare }}</template>
<template v-else>
<svg-icon v-if="isViewTag" name="octicon-tag"/>
<svg-icon v-else name="octicon-git-branch"/>
<strong ref="dropdownRefName" class="tw-ml-2">{{ refNameText }}</strong>
<strong ref="dropdownRefName" class="tw-ml-2 tw-inline-block gt-ellipsis">{{ refNameText }}</strong>
</template>
</span>
<svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>