1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-02-02 15:09:33 -05:00

Merge branch 'main' into agit/update

This commit is contained in:
hiifong 2025-01-13 14:14:51 +08:00 committed by GitHub
commit 2242557930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 272 additions and 295 deletions

View File

@ -88,7 +88,7 @@ func (run *ActionRun) RefLink() string {
if refName.IsPull() { if refName.IsPull() {
return run.Repo.Link() + "/pulls/" + refName.ShortName() return run.Repo.Link() + "/pulls/" + refName.ShortName()
} }
return git.RefURL(run.Repo.Link(), run.Ref) return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
} }
// PrettyRef return #id for pull ref or ShortName for others // PrettyRef return #id for pull ref or ShortName for others

View File

@ -355,7 +355,7 @@ func (a *Action) GetBranch() string {
// GetRefLink returns the action's ref link. // GetRefLink returns the action's ref link.
func (a *Action) GetRefLink(ctx context.Context) string { func (a *Action) GetRefLink(ctx context.Context) string {
return git.RefURL(a.GetRepoLink(ctx), a.RefName) return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
} }
// GetTag returns the action's repository tag. // GetTag returns the action's repository tag.

View File

@ -56,16 +56,11 @@ func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) {
if err != nil { if err != nil {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
} }
nameExts := strings.SplitN(parts[2], ".", 2) commitID, archiveType := git.SplitArchiveNameType(parts[2])
if len(nameExts) != 2 { if archiveType == git.ArchiveUnknown {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
} }
return &RepoArchiver{RepoID: repoID, CommitID: commitID, Type: archiveType}, nil
return &RepoArchiver{
RepoID: repoID,
CommitID: parts[1] + nameExts[0],
Type: git.ToArchiveType(nameExts[1]),
}, nil
} }
// GetRepoArchiver get an archiver // GetRepoArchiver get an archiver

View File

@ -476,8 +476,12 @@ func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSetting
} }
func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool { func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool {
minLen := util.OptionalArg(minLength, objFmt.FullLength()) maxLen := 64 // sha256
if len(s) < minLen || len(s) > objFmt.FullLength() { if objFmt != nil {
maxLen = objFmt.FullLength()
}
minLen := util.OptionalArg(minLength, maxLen)
if len(s) < minLen || len(s) > maxLen {
return false return false
} }
for _, c := range s { for _, c := range s {

View File

@ -81,6 +81,10 @@ func RefNameFromTag(shortName string) RefName {
return RefName(TagPrefix + shortName) return RefName(TagPrefix + shortName)
} }
func RefNameFromCommit(shortName string) RefName {
return RefName(shortName)
}
func (ref RefName) String() string { func (ref RefName) String() string {
return string(ref) return string(ref)
} }
@ -188,32 +192,38 @@ func (ref RefName) RefGroup() string {
return "" return ""
} }
// RefType is a simple ref type of the reference, it is used for UI and webhooks
type RefType string
const (
RefTypeBranch RefType = "branch"
RefTypeTag RefType = "tag"
RefTypeCommit RefType = "commit"
)
// RefType returns the simple ref type of the reference, e.g. branch, tag // RefType returns the simple ref type of the reference, e.g. branch, tag
// It's different from RefGroup, which is using the name of the directory under .git/refs // It's different from RefGroup, which is using the name of the directory under .git/refs
// Here we using branch but not heads, using tag but not tags func (ref RefName) RefType() RefType {
func (ref RefName) RefType() string { switch {
var refType string case ref.IsBranch():
if ref.IsBranch() { return RefTypeBranch
refType = "branch" case ref.IsTag():
} else if ref.IsTag() { return RefTypeTag
refType = "tag" case IsStringLikelyCommitID(nil, string(ref), 6):
return RefTypeCommit
} }
return refType return ""
} }
// RefURL returns the absolute URL for a ref in a repository // RefWebLinkPath returns a path for the reference that can be used in a web link:
func RefURL(repoURL, ref string) string { // * "branch/<branch_name>"
refFullName := RefName(ref) // * "tag/<tag_name>"
refName := util.PathEscapeSegments(refFullName.ShortName()) // * "commit/<commit_id>"
switch { // It returns an empty string if the reference is not a branch, tag or commit.
case refFullName.IsBranch(): func (ref RefName) RefWebLinkPath() string {
return repoURL + "/src/branch/" + refName refType := ref.RefType()
case refFullName.IsTag(): if refType == "" {
return repoURL + "/src/tag/" + refName return ""
case !Sha1ObjectFormat.IsValid(ref):
// assume they mean a branch
return repoURL + "/src/branch/" + refName
default:
return repoURL + "/src/commit/" + refName
} }
return string(refType) + "/" + util.PathEscapeSegments(ref.ShortName())
} }

View File

@ -44,9 +44,8 @@ func TestRefName(t *testing.T) {
assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName()) assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName())
} }
func TestRefURL(t *testing.T) { func TestRefWebLinkPath(t *testing.T) {
repoURL := "/user/repo" assert.Equal(t, "branch/foo", RefName("refs/heads/foo").RefWebLinkPath())
assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo")) assert.Equal(t, "tag/foo", RefName("refs/tags/foo").RefWebLinkPath())
assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo")) assert.Equal(t, "commit/c0ffee", RefName("c0ffee").RefWebLinkPath())
assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
} }

View File

@ -16,37 +16,35 @@ import (
type ArchiveType int type ArchiveType int
const ( const (
// ZIP zip archive type ArchiveUnknown ArchiveType = iota
ZIP ArchiveType = iota + 1 ArchiveZip // 1
// TARGZ tar gz archive type ArchiveTarGz // 2
TARGZ ArchiveBundle // 3
// BUNDLE bundle archive type
BUNDLE
) )
// String converts an ArchiveType to string // String converts an ArchiveType to string: the extension of the archive file without prefix dot
func (a ArchiveType) String() string { func (a ArchiveType) String() string {
switch a { switch a {
case ZIP: case ArchiveZip:
return "zip" return "zip"
case TARGZ: case ArchiveTarGz:
return "tar.gz" return "tar.gz"
case BUNDLE: case ArchiveBundle:
return "bundle" return "bundle"
} }
return "unknown" return "unknown"
} }
func ToArchiveType(s string) ArchiveType { func SplitArchiveNameType(s string) (string, ArchiveType) {
switch s { switch {
case "zip": case strings.HasSuffix(s, ".zip"):
return ZIP return strings.TrimSuffix(s, ".zip"), ArchiveZip
case "tar.gz": case strings.HasSuffix(s, ".tar.gz"):
return TARGZ return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
case "bundle": case strings.HasSuffix(s, ".bundle"):
return BUNDLE return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
} }
return 0 return s, ArchiveUnknown
} }
// CreateArchive create archive content to the target path // CreateArchive create archive content to the target path

View File

@ -0,0 +1,32 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestArchiveType(t *testing.T) {
name, archiveType := SplitArchiveNameType("test.tar.gz")
assert.Equal(t, "test", name)
assert.Equal(t, "tar.gz", archiveType.String())
name, archiveType = SplitArchiveNameType("a/b/test.zip")
assert.Equal(t, "a/b/test", name)
assert.Equal(t, "zip", archiveType.String())
name, archiveType = SplitArchiveNameType("1234.bundle")
assert.Equal(t, "1234", name)
assert.Equal(t, "bundle", archiveType.String())
name, archiveType = SplitArchiveNameType("test")
assert.Equal(t, "test", name)
assert.Equal(t, "unknown", archiveType.String())
name, archiveType = SplitArchiveNameType("test.xz")
assert.Equal(t, "test.xz", name)
assert.Equal(t, "unknown", archiveType.String())
}

View File

@ -1115,9 +1115,7 @@ blame.ignore_revs = Ignoring revisions in <a href="%s">.git-blame-ignore-revs</a
blame.ignore_revs.failed = Failed to ignore revisions in <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed = Failed to ignore revisions in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip = Shows a maximum of 30 users user_search_tooltip = Shows a maximum of 30 users
tree_path_not_found_commit = Path %[1]s doesn't exist in commit %[2]s tree_path_not_found = Path %[1]s doesn't exist in %[2]s
tree_path_not_found_branch = Path %[1]s doesn't exist in branch %[2]s
tree_path_not_found_tag = Path %[1]s doesn't exist in tag %[2]s
transfer.accept = Accept Transfer transfer.accept = Accept Transfer
transfer.accept_desc = Transfer to "%s" transfer.accept_desc = Transfer to "%s"

View File

@ -46,7 +46,7 @@ webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charg
webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer. webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer.
webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"` webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"`
webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande. webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande.
webauthn_error_duplicated=La clé de sécurité n'est pas autorisée pour cette demande. Veuillez vous assurer que la clé n'est pas déjà enregistrée. webauthn_error_duplicated=La clé de sécurité nest pas autorisée pour cette demande. Veuillez vous assurer que la clé nest pas déjà enregistrée.
webauthn_error_empty=Vous devez définir un nom pour cette clé. webauthn_error_empty=Vous devez définir un nom pour cette clé.
webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer. webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer.
webauthn_reload=Recharger webauthn_reload=Recharger
@ -469,7 +469,7 @@ sspi_auth_failed=Échec de l'authentification SSPI
password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous lutilisez ailleurs. password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous lutilisez ailleurs.
password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned
last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis. last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis.
signin_passkey=Se connecter avec une clé didentification (passkey) signin_passkey=Se connecter avec une clé daccès (passkey)
back_to_sign_in=Revenir à la page de connexion back_to_sign_in=Revenir à la page de connexion
[mail] [mail]
@ -818,7 +818,7 @@ manage_ssh_keys=Gérer les clés SSH
manage_ssh_principals=Gérer les certificats principaux SSH manage_ssh_principals=Gérer les certificats principaux SSH
manage_gpg_keys=Gérer les clés GPG manage_gpg_keys=Gérer les clés GPG
add_key=Ajouter une clé add_key=Ajouter une clé
ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos. ssh_desc=Ces clés SSH publiques sont associées à votre compte. Les clés privées correspondantes permettent laccès complet à vos dépôts.
principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts. principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts.
gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions. gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions.
ssh_helper=<strong>Besoin d'aide ?</strong> Consultez le guide de GitHub pour <a href="%s">créer vos propres clés SSH</a> ou <a href="%s">résoudre les problèmes courants</a> que vous pourriez rencontrer en utilisant SSH. ssh_helper=<strong>Besoin d'aide ?</strong> Consultez le guide de GitHub pour <a href="%s">créer vos propres clés SSH</a> ou <a href="%s">résoudre les problèmes courants</a> que vous pourriez rencontrer en utilisant SSH.
@ -969,7 +969,7 @@ passcode_invalid=Le mot de passe est invalide. Réessayez.
twofa_enrolled=Lauthentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois. twofa_enrolled=Lauthentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.
twofa_failed_get_secret=Impossible d'obtenir le secret. twofa_failed_get_secret=Impossible d'obtenir le secret.
webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour lauthentification à deux facteurs. La clef de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>. webauthn_desc=Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour lauthentification à deux facteurs. La clé de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
webauthn_register_key=Ajouter une clé de sécurité webauthn_register_key=Ajouter une clé de sécurité
webauthn_nickname=Pseudonyme webauthn_nickname=Pseudonyme
webauthn_delete_key=Retirer la clé de sécurité webauthn_delete_key=Retirer la clé de sécurité
@ -2400,17 +2400,17 @@ settings.packagist_api_token=Jeton API
settings.packagist_package_url=URL du paquet Packagist settings.packagist_package_url=URL du paquet Packagist
settings.deploy_keys=Clés de déploiement settings.deploy_keys=Clés de déploiement
settings.add_deploy_key=Ajouter une clé de déploiement settings.add_deploy_key=Ajouter une clé de déploiement
settings.deploy_key_desc=Les clefs de déploiement ont un accès en lecture seule au dépôt. settings.deploy_key_desc=Les clés de déploiement ont un accès en lecture seule au dépôt.
settings.is_writable=Activer l'accès en écriture settings.is_writable=Activer l'accès en écriture
settings.is_writable_info=Autoriser cette clé de déploiement à <strong>soumettre</strong> sur le dépôt. settings.is_writable_info=Autoriser cette clé de déploiement à <strong>soumettre</strong> sur le dépôt.
settings.no_deploy_keys=Il n'y a pas encore de clefs de déploiement. settings.no_deploy_keys=Il ny a pas encore de clés de déploiement.
settings.title=Titre settings.title=Titre
settings.deploy_key_content=Contenu settings.deploy_key_content=Contenu
settings.key_been_used=Une clef de déploiement identique est déjà en cours d'utilisation. settings.key_been_used=Une clé de déploiement identique est déjà en cours dutilisation.
settings.key_name_used=Une clef de déploiement du même nom existe déjà. settings.key_name_used=Une clé de déploiement du même nom existe déjà.
settings.add_key_success=La clé de déploiement "%s" a été ajoutée. settings.add_key_success=La clé de déploiement « %s » a été ajoutée.
settings.deploy_key_deletion=Supprimer une clef de déploiement settings.deploy_key_deletion=Supprimer une clé de déploiement
settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ? settings.deploy_key_deletion_desc=La suppression dune clé de déploiement révoque son accès à ce dépôt. Continuer ?
settings.deploy_key_deletion_success=La clé de déploiement a été supprimée. settings.deploy_key_deletion_success=La clé de déploiement a été supprimée.
settings.branches=Branches settings.branches=Branches
settings.protected_branch=Protection de branche settings.protected_branch=Protection de branche
@ -2627,6 +2627,9 @@ diff.image.overlay=Superposition
diff.has_escaped=Cette ligne contient des caractères Unicode cachés diff.has_escaped=Cette ligne contient des caractères Unicode cachés
diff.show_file_tree=Afficher larborescence des fichiers diff.show_file_tree=Afficher larborescence des fichiers
diff.hide_file_tree=Masquer larborescence des fichiers diff.hide_file_tree=Masquer larborescence des fichiers
diff.submodule_added=Sous-module %[1]s ajouté à %[2]s
diff.submodule_deleted=Sous-module %[1]s supprimé de %[2]s
diff.submodule_updated=Sous-module %[1]s mis-à-jour : %[2]s
releases.desc=Suivi des publications et des téléchargements. releases.desc=Suivi des publications et des téléchargements.
release.releases=Publications release.releases=Publications
@ -3116,7 +3119,7 @@ auths.attribute_username_placeholder=Laisser vide afin d'utiliser le nom d'utili
auths.attribute_name=Attribut prénom auths.attribute_name=Attribut prénom
auths.attribute_surname=Attribut nom de famille auths.attribute_surname=Attribut nom de famille
auths.attribute_mail=Attribut e-mail auths.attribute_mail=Attribut e-mail
auths.attribute_ssh_public_key=Attribut clef SSH publique auths.attribute_ssh_public_key=Attribut clé SSH publique
auths.attribute_avatar=Attribut de l'avatar auths.attribute_avatar=Attribut de l'avatar
auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN
auths.allow_deactivate_all=Permettre à un résultat de recherche vide de désactiver tous les utilisateurs auths.allow_deactivate_all=Permettre à un résultat de recherche vide de désactiver tous les utilisateurs
@ -3240,7 +3243,7 @@ config.ssh_port=Port
config.ssh_listen_port=Port d'écoute config.ssh_listen_port=Port d'écoute
config.ssh_root_path=Emplacement racine config.ssh_root_path=Emplacement racine
config.ssh_key_test_path=Chemin de test des clés config.ssh_key_test_path=Chemin de test des clés
config.ssh_keygen_path=Chemin vers le générateur de clefs ("ssh-keygen") config.ssh_keygen_path=Chemin vers le générateur de clés (« ssh-keygen »)
config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale
config.ssh_minimum_key_sizes=Tailles de clé minimales config.ssh_minimum_key_sizes=Tailles de clé minimales

View File

@ -1015,6 +1015,7 @@ new_repo_helper=リポジトリには、プロジェクトのすべてのファ
owner=オーナー owner=オーナー
owner_helper=リポジトリ数の上限により、一部の組織はドロップダウンに表示されない場合があります。 owner_helper=リポジトリ数の上限により、一部の組織はドロップダウンに表示されない場合があります。
repo_name=リポジトリ名 repo_name=リポジトリ名
repo_name_helper=リポジトリ名は、短く、覚えやすく、他と重複しないキーワードを使用しましょう。 リポジトリ名を ".profile" または ".profile-private" にして README.md を追加すると、ユーザーや組織のプロフィールとなります。
repo_size=リポジトリサイズ repo_size=リポジトリサイズ
template=テンプレート template=テンプレート
template_select=テンプレートを選択してください。 template_select=テンプレートを選択してください。
@ -2852,6 +2853,8 @@ teams.invite.title=あなたは組織 <strong>%[2]s</strong> 内のチーム <st
teams.invite.by=%s からの招待 teams.invite.by=%s からの招待
teams.invite.description=下のボタンをクリックしてチームに参加してください。 teams.invite.description=下のボタンをクリックしてチームに参加してください。
view_as_public_hint=READMEを公開ユーザーとして見ています。
view_as_member_hint=READMEをこの組織のメンバーとして見ています。
[admin] [admin]
maintenance=メンテナンス maintenance=メンテナンス
@ -3530,6 +3533,8 @@ alpine.repository=リポジトリ情報
alpine.repository.branches=Branches alpine.repository.branches=Branches
alpine.repository.repositories=Repositories alpine.repository.repositories=Repositories
alpine.repository.architectures=Architectures alpine.repository.architectures=Architectures
arch.registry=<code>/etc/pacman.conf</code> にリポジトリとアーキテクチャを含めてサーバーを追加します:
arch.install=pacmanでパッケージを同期します:
arch.repository=リポジトリ情報 arch.repository=リポジトリ情報
arch.repository.repositories=リポジトリ arch.repository.repositories=リポジトリ
arch.repository.architectures=Architectures arch.repository.architectures=Architectures
@ -3712,6 +3717,7 @@ runners.status.active=稼働中
runners.status.offline=オフライン runners.status.offline=オフライン
runners.version=バージョン runners.version=バージョン
runners.reset_registration_token=登録トークンをリセット runners.reset_registration_token=登録トークンをリセット
runners.reset_registration_token_confirm=現在のトークンを無効にして、新しいトークンを生成しますか?
runners.reset_registration_token_success=ランナー登録トークンをリセットしました runners.reset_registration_token_success=ランナー登録トークンをリセットしました
runs.all_workflows=すべてのワークフロー runs.all_workflows=すべてのワークフロー

View File

@ -120,7 +120,7 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
"ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1. "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
"ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1. "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
"ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run. "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
"ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag. "ref_type": string(refName.RefType()), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
"path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions." "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
"repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World. "repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
"repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat. "repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.

View File

@ -17,11 +17,11 @@ func DownloadArchive(ctx *context.APIContext) {
var tp git.ArchiveType var tp git.ArchiveType
switch ballType := ctx.PathParam("ball_type"); ballType { switch ballType := ctx.PathParam("ball_type"); ballType {
case "tarball": case "tarball":
tp = git.TARGZ tp = git.ArchiveTarGz
case "zipball": case "zipball":
tp = git.ZIP tp = git.ArchiveZip
case "bundle": case "bundle":
tp = git.BUNDLE tp = git.ArchiveBundle
default: default:
ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType)) ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType))
return return
@ -36,7 +36,7 @@ func DownloadArchive(ctx *context.APIContext) {
} }
} }
r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"), tp) r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")+"."+tp.String())
if err != nil { if err != nil {
ctx.ServerError("NewRequest", err) ctx.ServerError("NewRequest", err)
return return

View File

@ -293,14 +293,7 @@ func GetArchive(ctx *context.APIContext) {
} }
func archiveDownload(ctx *context.APIContext) { func archiveDownload(ctx *context.APIContext) {
uri := ctx.PathParam("*") aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
ext, tp, err := archiver_service.ParseFileName(uri)
if err != nil {
ctx.Error(http.StatusBadRequest, "ParseFileName", err)
return
}
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
if err != nil { if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
ctx.Error(http.StatusBadRequest, "unknown archive format", err) ctx.Error(http.StatusBadRequest, "unknown archive format", err)

View File

@ -24,7 +24,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
} }
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{ git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.RefName, Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: fileName, File: fileName,
Page: 1, Page: 1,
}) })

View File

@ -222,7 +222,7 @@ func FileHistory(ctx *context.Context) {
return return
} }
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefName, fileName) commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), fileName) // FIXME: legacy code used ShortName
if err != nil { if err != nil {
ctx.ServerError("FileCommitsCount", err) ctx.ServerError("FileCommitsCount", err)
return return
@ -238,7 +238,7 @@ func FileHistory(ctx *context.Context) {
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{ git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.RefName, Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: fileName, File: fileName,
Page: page, Page: page,
}) })

View File

@ -4,25 +4,14 @@
package repo package repo
import ( import (
"net/url"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
func HandleGitError(ctx *context.Context, msg string, err error) { func HandleGitError(ctx *context.Context, msg string, err error) {
if git.IsErrNotExist(err) { if git.IsErrNotExist(err) {
refType := "" ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found", ctx.Repo.TreePath, ctx.Repo.RefTypeNameSubURL())
switch { ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
case ctx.Repo.IsViewBranch:
refType = "branch"
case ctx.Repo.IsViewTag:
refType = "tag"
case ctx.Repo.IsViewCommit:
refType = "commit"
}
ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found_"+refType, ctx.Repo.TreePath, url.PathEscape(ctx.Repo.RefName))
ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + refType + "/" + url.PathEscape(ctx.Repo.RefName)
ctx.NotFound(msg, err) ctx.NotFound(msg, err)
} else { } else {
ctx.ServerError(msg, err) ctx.ServerError(msg, err)

View File

@ -463,13 +463,7 @@ func RedirectDownload(ctx *context.Context) {
// Download an archive of a repository // Download an archive of a repository
func Download(ctx *context.Context) { func Download(ctx *context.Context) {
uri := ctx.PathParam("*") aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
ext, tp, err := archiver_service.ParseFileName(uri)
if err != nil {
ctx.ServerError("ParseFileName", err)
return
}
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
if err != nil { if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
ctx.Error(http.StatusBadRequest, err.Error()) ctx.Error(http.StatusBadRequest, err.Error())
@ -527,15 +521,9 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
// a request that's already in-progress, but the archiver service will just // a request that's already in-progress, but the archiver service will just
// kind of drop it on the floor if this is the case. // kind of drop it on the floor if this is the case.
func InitiateDownload(ctx *context.Context) { func InitiateDownload(ctx *context.Context) {
uri := ctx.PathParam("*") aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
ext, tp, err := archiver_service.ParseFileName(uri)
if err != nil { if err != nil {
ctx.ServerError("ParseFileName", err) ctx.Error(http.StatusBadRequest, "invalid archive request")
return
}
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
if err != nil {
ctx.ServerError("archiver_service.NewRequest", err)
return return
} }
if aReq == nil { if aReq == nil {

View File

@ -42,7 +42,7 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
} }
defer dataRc.Close() defer dataRc.Close()
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName())
ctx.Data["FileIsSymlink"] = entry.IsLink() ctx.Data["FileIsSymlink"] = entry.IsLink()
ctx.Data["FileName"] = blob.Name() ctx.Data["FileName"] = blob.Name()
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)

View File

@ -135,7 +135,7 @@ func prepareToRenderDirectory(ctx *context.Context) {
if ctx.Repo.TreePath != "" { if ctx.Repo.TreePath != "" {
ctx.Data["HideRepoInfo"] = true ctx.Data["HideRepoInfo"] = true
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName())
} }
subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true) subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)

View File

@ -1572,6 +1572,7 @@ func registerRoutes(m *web.Router) {
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed) m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
m.Group("/src", func() { m.Group("/src", func() {
m.Get("", func(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink) }) // there is no "{owner}/{repo}/src" page, so redirect to "{owner}/{repo}" to avoid 404
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)

View File

@ -563,9 +563,9 @@ func (n *actionsNotifier) CreateRef(ctx context.Context, pusher *user_model.User
newNotifyInput(repo, pusher, webhook_module.HookEventCreate). newNotifyInput(repo, pusher, webhook_module.HookEventCreate).
WithRef(refFullName.String()). WithRef(refFullName.String()).
WithPayload(&api.CreatePayload{ WithPayload(&api.CreatePayload{
Ref: refFullName.String(), Ref: refFullName.String(), // HINT: here is inconsistent with the Webhook's payload: webhook uses ShortName
Sha: refID, Sha: refID,
RefType: refFullName.RefType(), RefType: string(refFullName.RefType()),
Repo: apiRepo, Repo: apiRepo,
Sender: apiPusher, Sender: apiPusher,
}). }).
@ -580,8 +580,8 @@ func (n *actionsNotifier) DeleteRef(ctx context.Context, pusher *user_model.User
newNotifyInput(repo, pusher, webhook_module.HookEventDelete). newNotifyInput(repo, pusher, webhook_module.HookEventDelete).
WithPayload(&api.DeletePayload{ WithPayload(&api.DeletePayload{
Ref: refFullName.String(), Ref: refFullName.String(), // HINT: here is inconsistent with the Webhook's payload: webhook uses ShortName
RefType: refFullName.RefType(), RefType: string(refFullName.RefType()),
PusherType: api.PusherTypeUser, PusherType: api.PusherTypeUser,
Repo: apiRepo, Repo: apiRepo,
Sender: apiPusher, Sender: apiPusher,

View File

@ -58,10 +58,10 @@ type Repository struct {
IsViewTag bool IsViewTag bool
IsViewCommit bool IsViewCommit bool
RefName string RefFullName git.RefName
BranchName string BranchName string
TagName string TagName string
TreePath string TreePath string
// Commit it is always set to the commit for the branch or tag // Commit it is always set to the commit for the branch or tag
Commit *git.Commit Commit *git.Commit
@ -210,16 +210,7 @@ func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool,
// * "commit/123456" // * "commit/123456"
// It is usually used to construct a link like ".../src/{{RefTypeNameSubURL}}/{{PathEscapeSegments TreePath}}" // It is usually used to construct a link like ".../src/{{RefTypeNameSubURL}}/{{PathEscapeSegments TreePath}}"
func (r *Repository) RefTypeNameSubURL() string { func (r *Repository) RefTypeNameSubURL() string {
switch { return r.RefFullName.RefWebLinkPath()
case r.IsViewBranch:
return "branch/" + util.PathEscapeSegments(r.BranchName)
case r.IsViewTag:
return "tag/" + util.PathEscapeSegments(r.TagName)
case r.IsViewCommit:
return "commit/" + util.PathEscapeSegments(r.CommitID)
}
log.Error("Unknown view type for repo: %v", r)
return ""
} }
// GetEditorconfig returns the .editorconfig definition if found in the // GetEditorconfig returns the .editorconfig definition if found in the
@ -392,33 +383,25 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
// RepoAssignment returns a middleware to handle repository assignment // RepoAssignment returns a middleware to handle repository assignment
func RepoAssignment(ctx *Context) { func RepoAssignment(ctx *Context) {
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce { if ctx.Data["Repository"] != nil {
// FIXME: it should panic in dev/test modes to have a clear behavior setting.PanicInDevOrTesting("RepoAssignment should not be executed twice")
if !setting.IsProd || setting.IsInTesting {
panic("RepoAssignment should not be executed twice")
}
return
} }
ctx.Data["repoAssignmentExecuted"] = true
var (
owner *user_model.User
err error
)
var err error
userName := ctx.PathParam("username") userName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame") repoName := ctx.PathParam("reponame")
repoName = strings.TrimSuffix(repoName, ".git") repoName = strings.TrimSuffix(repoName, ".git")
if setting.Other.EnableFeed { if setting.Other.EnableFeed {
ctx.Data["EnableFeed"] = true
repoName = strings.TrimSuffix(repoName, ".rss") repoName = strings.TrimSuffix(repoName, ".rss")
repoName = strings.TrimSuffix(repoName, ".atom") repoName = strings.TrimSuffix(repoName, ".atom")
} }
// Check if the user is the same as the repository owner // Check if the user is the same as the repository owner
if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) { if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) {
owner = ctx.Doer ctx.Repo.Owner = ctx.Doer
} else { } else {
owner, err = user_model.GetUserByName(ctx, userName) ctx.Repo.Owner, err = user_model.GetUserByName(ctx, userName)
if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { if user_model.IsErrUserNotExist(err) {
// go-get does not support redirects // go-get does not support redirects
@ -441,8 +424,7 @@ func RepoAssignment(ctx *Context) {
return return
} }
} }
ctx.Repo.Owner = owner ctx.ContextUser = ctx.Repo.Owner
ctx.ContextUser = owner
ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["ContextUser"] = ctx.ContextUser
// redirect link to wiki // redirect link to wiki
@ -466,10 +448,10 @@ func RepoAssignment(ctx *Context) {
} }
// Get repository. // Get repository.
repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) repo, err := repo_model.GetRepositoryByName(ctx, ctx.Repo.Owner.ID, repoName)
if err != nil { if err != nil {
if repo_model.IsErrRepoNotExist(err) { if repo_model.IsErrRepoNotExist(err) {
redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName) redirectRepoID, err := repo_model.LookupRedirect(ctx, ctx.Repo.Owner.ID, repoName)
if err == nil { if err == nil {
RedirectToRepo(ctx.Base, redirectRepoID) RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) { } else if repo_model.IsErrRedirectNotExist(err) {
@ -486,7 +468,7 @@ func RepoAssignment(ctx *Context) {
} }
return return
} }
repo.Owner = owner repo.Owner = ctx.Repo.Owner
repoAssignment(ctx, repo) repoAssignment(ctx, repo)
if ctx.Written() { if ctx.Written() {
@ -495,11 +477,7 @@ func RepoAssignment(ctx *Context) {
ctx.Repo.RepoLink = repo.Link() ctx.Repo.RepoLink = repo.Link()
ctx.Data["RepoLink"] = ctx.Repo.RepoLink ctx.Data["RepoLink"] = ctx.Repo.RepoLink
ctx.Data["FeedURL"] = ctx.Repo.RepoLink
if setting.Other.EnableFeed {
ctx.Data["EnableFeed"] = true
ctx.Data["FeedURL"] = ctx.Repo.RepoLink
}
unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker) unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker)
if err == nil { if err == nil {
@ -526,7 +504,7 @@ func RepoAssignment(ctx *Context) {
return return
} }
ctx.Data["Title"] = owner.Name + "/" + repo.Name ctx.Data["Title"] = repo.Owner.Name + "/" + repo.Name
ctx.Data["Repository"] = repo ctx.Data["Repository"] = repo
ctx.Data["Owner"] = ctx.Repo.Repository.Owner ctx.Data["Owner"] = ctx.Repo.Repository.Owner
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode) ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
@ -596,7 +574,6 @@ func RepoAssignment(ctx *Context) {
// Disable everything when the repo is being created // Disable everything when the repo is being created
if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() { if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
if !isHomeOrSettings { if !isHomeOrSettings {
ctx.Redirect(ctx.Repo.RepoLink) ctx.Redirect(ctx.Repo.RepoLink)
} }
@ -604,9 +581,7 @@ func RepoAssignment(ctx *Context) {
} }
if ctx.Repo.GitRepo != nil { if ctx.Repo.GitRepo != nil {
if !setting.IsProd || setting.IsInTesting { setting.PanicInDevOrTesting("RepoAssignment: GitRepo should be nil")
panic("RepoAssignment: GitRepo should be nil")
}
_ = ctx.Repo.GitRepo.Close() _ = ctx.Repo.GitRepo.Close()
ctx.Repo.GitRepo = nil ctx.Repo.GitRepo = nil
} }
@ -616,7 +591,6 @@ func RepoAssignment(ctx *Context) {
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") { if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err) log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
ctx.Repo.Repository.MarkAsBrokenEmpty() ctx.Repo.Repository.MarkAsBrokenEmpty()
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
// Only allow access to base of repo or settings // Only allow access to base of repo or settings
if !isHomeOrSettings { if !isHomeOrSettings {
ctx.Redirect(ctx.Repo.RepoLink) ctx.Redirect(ctx.Repo.RepoLink)
@ -629,7 +603,6 @@ func RepoAssignment(ctx *Context) {
// Stop at this point when the repo is empty. // Stop at this point when the repo is empty.
if ctx.Repo.Repository.IsEmpty { if ctx.Repo.Repository.IsEmpty {
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
return return
} }
@ -655,22 +628,6 @@ func RepoAssignment(ctx *Context) {
ctx.Data["BranchesCount"] = branchesTotal ctx.Data["BranchesCount"] = branchesTotal
// If no branch is set in the request URL, try to guess a default one.
if len(ctx.Repo.BranchName) == 0 {
if len(ctx.Repo.Repository.DefaultBranch) > 0 && ctx.Repo.GitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
} else {
ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository)
if ctx.Repo.BranchName == "" {
// If it still can't get a default branch, fall back to default branch from setting.
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
ctx.Repo.BranchName = setting.Repository.DefaultBranch
}
}
ctx.Repo.RefName = ctx.Repo.BranchName
}
ctx.Data["BranchName"] = ctx.Repo.BranchName
// People who have push access or have forked repository can propose a new pull request. // People who have push access or have forked repository can propose a new pull request.
canPush := ctx.Repo.CanWrite(unit_model.TypeCode) || canPush := ctx.Repo.CanWrite(unit_model.TypeCode) ||
(ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)) (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
@ -713,7 +670,7 @@ func RepoAssignment(ctx *Context) {
} }
if ctx.FormString("go-get") == "1" { if ctx.FormString("go-get") == "1" {
ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, owner.Name, repo.Name) ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, repo.Owner.Name, repo.Name)
fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName) fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}" ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}" ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
@ -844,26 +801,39 @@ type RepoRefByTypeOptions struct {
IgnoreNotExistErr bool IgnoreNotExistErr bool
} }
func repoRefFullName(shortName string, typ RepoRefType) git.RefName {
switch typ {
case RepoRefBranch:
return git.RefNameFromBranch(shortName)
case RepoRefTag:
return git.RefNameFromTag(shortName)
case RepoRefCommit:
return git.RefNameFromCommit(shortName)
default:
setting.PanicInDevOrTesting("Unknown RepoRefType: %v", typ)
return git.RefNameFromBranch("main") // just a dummy result, it shouldn't happen
}
}
// RepoRefByType handles repository reference name for a specific type // RepoRefByType handles repository reference name for a specific type
// of repository reference // of repository reference
func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) { func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) {
opt := util.OptionalArg(opts) opt := util.OptionalArg(opts)
return func(ctx *Context) { return func(ctx *Context) {
var err error
refType := detectRefType refType := detectRefType
// Empty repository does not have reference information. // Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty { if ctx.Repo.Repository.IsEmpty {
// assume the user is viewing the (non-existent) default branch // assume the user is viewing the (non-existent) default branch
ctx.Repo.IsViewBranch = true ctx.Repo.IsViewBranch = true
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
ctx.Repo.RefFullName = git.RefNameFromBranch(ctx.Repo.BranchName)
// these variables are used by the template to "add/upload" new files
ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["TreePath"] = "" ctx.Data["TreePath"] = ""
return return
} }
var (
refName string
err error
)
if ctx.Repo.GitRepo == nil { if ctx.Repo.GitRepo == nil {
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository) ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil { if err != nil {
@ -873,22 +843,23 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
} }
// Get default branch. // Get default branch.
var refShortName string
reqPath := ctx.PathParam("*") reqPath := ctx.PathParam("*")
if reqPath == "" { if reqPath == "" {
refName = ctx.Repo.Repository.DefaultBranch refShortName = ctx.Repo.Repository.DefaultBranch
if !ctx.Repo.GitRepo.IsBranchExist(refName) { if !ctx.Repo.GitRepo.IsBranchExist(refShortName) {
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1) brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
if err == nil && len(brs) != 0 { if err == nil && len(brs) != 0 {
refName = brs[0].Name refShortName = brs[0].Name
} else if len(brs) == 0 { } else if len(brs) == 0 {
log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path) log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
} else { } else {
log.Error("GetBranches error: %v", err) log.Error("GetBranches error: %v", err)
} }
} }
ctx.Repo.RefName = refName ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
ctx.Repo.BranchName = refName ctx.Repo.BranchName = refShortName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
if err == nil { if err == nil {
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") { } else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
@ -902,35 +873,37 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
} else { // there is a path in request } else { // there is a path in request
guessLegacyPath := refType == RepoRefUnknown guessLegacyPath := refType == RepoRefUnknown
if guessLegacyPath { if guessLegacyPath {
refName, refType = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "") refShortName, refType = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "")
} else { } else {
refName = getRefName(ctx.Base, ctx.Repo, reqPath, refType) refShortName = getRefName(ctx.Base, ctx.Repo, reqPath, refType)
} }
ctx.Repo.RefName = refName ctx.Repo.RefFullName = repoRefFullName(refShortName, refType)
isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool) isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
if isRenamedBranch && has { if isRenamedBranch && has {
renamedBranchName := ctx.Data["RenamedBranchName"].(string) renamedBranchName := ctx.Data["RenamedBranchName"].(string)
ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName)) ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refShortName, renamedBranchName))
link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1) link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refShortName), util.PathEscapeSegments(renamedBranchName), 1)
ctx.Redirect(link) ctx.Redirect(link)
return return
} }
if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refName) { if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refShortName) {
ctx.Repo.IsViewBranch = true ctx.Repo.IsViewBranch = true
ctx.Repo.BranchName = refName ctx.Repo.BranchName = refShortName
ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
if err != nil { if err != nil {
ctx.ServerError("GetBranchCommit", err) ctx.ServerError("GetBranchCommit", err)
return return
} }
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refName) { } else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refShortName) {
ctx.Repo.IsViewTag = true ctx.Repo.IsViewTag = true
ctx.Repo.TagName = refName ctx.Repo.RefFullName = git.RefNameFromTag(refShortName)
ctx.Repo.TagName = refShortName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName) ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refShortName)
if err != nil { if err != nil {
if git.IsErrNotExist(err) { if git.IsErrNotExist(err) {
ctx.NotFound("GetTagCommit", err) ctx.NotFound("GetTagCommit", err)
@ -940,25 +913,26 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
return return
} }
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) { } else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refShortName, 7) {
ctx.Repo.IsViewCommit = true ctx.Repo.IsViewCommit = true
ctx.Repo.CommitID = refName ctx.Repo.RefFullName = git.RefNameFromCommit(refShortName)
ctx.Repo.CommitID = refShortName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refShortName)
if err != nil { if err != nil {
ctx.NotFound("GetCommit", err) ctx.NotFound("GetCommit", err)
return return
} }
// If short commit ID add canonical link header // If short commit ID add canonical link header
if len(refName) < ctx.Repo.GetObjectFormat().FullLength() { if len(refShortName) < ctx.Repo.GetObjectFormat().FullLength() {
canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1)) canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refShortName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL)) ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL))
} }
} else { } else {
if opt.IgnoreNotExistErr { if opt.IgnoreNotExistErr {
return return
} }
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refShortName))
return return
} }
@ -975,15 +949,19 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
} }
} }
ctx.Data["BranchName"] = ctx.Repo.BranchName ctx.Data["RefFullName"] = ctx.Repo.RefFullName
ctx.Data["RefName"] = ctx.Repo.RefName
ctx.Data["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL() ctx.Data["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL()
ctx.Data["TagName"] = ctx.Repo.TagName
ctx.Data["CommitID"] = ctx.Repo.CommitID
ctx.Data["TreePath"] = ctx.Repo.TreePath ctx.Data["TreePath"] = ctx.Repo.TreePath
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
ctx.Data["TagName"] = ctx.Repo.TagName
ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
ctx.Data["CommitID"] = ctx.Repo.CommitID
ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()

View File

@ -469,7 +469,7 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release
Repo: rel.Repo, Repo: rel.Repo,
IsPrivate: rel.Repo.IsPrivate, IsPrivate: rel.Repo.IsPrivate,
Content: rel.Title, Content: rel.Title,
RefName: rel.TagName, // FIXME: use a full ref name? RefName: git.RefNameFromTag(rel.TagName).String(), // Other functions in this file all use "refFullName.String()"
}); err != nil { }); err != nil {
log.Error("NotifyWatchers: %v", err) log.Error("NotifyWatchers: %v", err)
} }

View File

@ -250,8 +250,9 @@ func GetRefEndNamesAndURLs(issues []*issues_model.Issue, repoLink string) (map[i
issueRefURLs := make(map[int64]string, len(issues)) issueRefURLs := make(map[int64]string, len(issues))
for _, issue := range issues { for _, issue := range issues {
if issue.Ref != "" { if issue.Ref != "" {
issueRefEndNames[issue.ID] = git.RefName(issue.Ref).ShortName() ref := git.RefName(issue.Ref)
issueRefURLs[issue.ID] = git.RefURL(repoLink, issue.Ref) issueRefEndNames[issue.ID] = ref.ShortName()
issueRefURLs[issue.ID] = repoLink + "/src/" + ref.RefWebLinkPath()
} }
} }
return issueRefEndNames, issueRefURLs return issueRefEndNames, issueRefURLs

View File

@ -31,19 +31,20 @@ import (
// handle elsewhere. // handle elsewhere.
type ArchiveRequest struct { type ArchiveRequest struct {
RepoID int64 RepoID int64
refName string
Type git.ArchiveType Type git.ArchiveType
CommitID string CommitID string
archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id"
} }
// ErrUnknownArchiveFormat request archive format is not supported // ErrUnknownArchiveFormat request archive format is not supported
type ErrUnknownArchiveFormat struct { type ErrUnknownArchiveFormat struct {
RequestFormat string RequestNameType string
} }
// Error implements error // Error implements error
func (err ErrUnknownArchiveFormat) Error() string { func (err ErrUnknownArchiveFormat) Error() string {
return fmt.Sprintf("unknown format: %s", err.RequestFormat) return fmt.Sprintf("unknown format: %s", err.RequestNameType)
} }
// Is implements error // Is implements error
@ -54,12 +55,12 @@ func (ErrUnknownArchiveFormat) Is(err error) bool {
// RepoRefNotFoundError is returned when a requested reference (commit, tag) was not found. // RepoRefNotFoundError is returned when a requested reference (commit, tag) was not found.
type RepoRefNotFoundError struct { type RepoRefNotFoundError struct {
RefName string RefShortName string
} }
// Error implements error. // Error implements error.
func (e RepoRefNotFoundError) Error() string { func (e RepoRefNotFoundError) Error() string {
return fmt.Sprintf("unrecognized repository reference: %s", e.RefName) return fmt.Sprintf("unrecognized repository reference: %s", e.RefShortName)
} }
func (e RepoRefNotFoundError) Is(err error) bool { func (e RepoRefNotFoundError) Is(err error) bool {
@ -67,43 +68,23 @@ func (e RepoRefNotFoundError) Is(err error) bool {
return ok return ok
} }
func ParseFileName(uri string) (ext string, tp git.ArchiveType, err error) {
switch {
case strings.HasSuffix(uri, ".zip"):
ext = ".zip"
tp = git.ZIP
case strings.HasSuffix(uri, ".tar.gz"):
ext = ".tar.gz"
tp = git.TARGZ
case strings.HasSuffix(uri, ".bundle"):
ext = ".bundle"
tp = git.BUNDLE
default:
return "", 0, ErrUnknownArchiveFormat{RequestFormat: uri}
}
return ext, tp, nil
}
// NewRequest creates an archival request, based on the URI. The // NewRequest creates an archival request, based on the URI. The
// resulting ArchiveRequest is suitable for being passed to Await() // resulting ArchiveRequest is suitable for being passed to Await()
// if it's determined that the request still needs to be satisfied. // if it's determined that the request still needs to be satisfied.
func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git.ArchiveType) (*ArchiveRequest, error) { func NewRequest(repoID int64, repo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) {
if fileType < git.ZIP || fileType > git.BUNDLE { // here the archiveRefShortName is not a clear ref, it could be a tag, branch or commit id
return nil, ErrUnknownArchiveFormat{RequestFormat: fileType.String()} archiveRefShortName, archiveType := git.SplitArchiveNameType(archiveRefExt)
} if archiveType == git.ArchiveUnknown {
return nil, ErrUnknownArchiveFormat{archiveRefExt}
r := &ArchiveRequest{
RepoID: repoID,
refName: refName,
Type: fileType,
} }
// Get corresponding commit. // Get corresponding commit.
commitID, err := repo.ConvertToGitID(r.refName) commitID, err := repo.ConvertToGitID(archiveRefShortName)
if err != nil { if err != nil {
return nil, RepoRefNotFoundError{RefName: r.refName} return nil, RepoRefNotFoundError{RefShortName: archiveRefShortName}
} }
r := &ArchiveRequest{RepoID: repoID, archiveRefShortName: archiveRefShortName, Type: archiveType}
r.CommitID = commitID.String() r.CommitID = commitID.String()
return r, nil return r, nil
} }
@ -111,11 +92,11 @@ func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git
// GetArchiveName returns the name of the caller, based on the ref used by the // GetArchiveName returns the name of the caller, based on the ref used by the
// caller to create this request. // caller to create this request.
func (aReq *ArchiveRequest) GetArchiveName() string { func (aReq *ArchiveRequest) GetArchiveName() string {
return strings.ReplaceAll(aReq.refName, "/", "-") + "." + aReq.Type.String() return strings.ReplaceAll(aReq.archiveRefShortName, "/", "-") + "." + aReq.Type.String()
} }
// Await awaits the completion of an ArchiveRequest. If the archive has // Await awaits the completion of an ArchiveRequest. If the archive has
// already been prepared the method returns immediately. Otherwise an archiver // already been prepared the method returns immediately. Otherwise, an archiver
// process will be started and its completion awaited. On success the returned // process will be started and its completion awaited. On success the returned
// RepoArchiver may be used to download the archive. Note that even if the // RepoArchiver may be used to download the archive. Note that even if the
// context is cancelled/times out a started archiver will still continue to run // context is cancelled/times out a started archiver will still continue to run
@ -208,8 +189,8 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
rd, w := io.Pipe() rd, w := io.Pipe()
defer func() { defer func() {
w.Close() _ = w.Close()
rd.Close() _ = rd.Close()
}() }()
done := make(chan error, 1) // Ensure that there is some capacity which will ensure that the goroutine below can always finish done := make(chan error, 1) // Ensure that there is some capacity which will ensure that the goroutine below can always finish
repo, err := repo_model.GetRepositoryByID(ctx, archiver.RepoID) repo, err := repo_model.GetRepositoryByID(ctx, archiver.RepoID)
@ -230,7 +211,7 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
} }
}() }()
if archiver.Type == git.BUNDLE { if archiver.Type == git.ArchiveBundle {
err = gitRepo.CreateBundle( err = gitRepo.CreateBundle(
ctx, ctx,
archiver.CommitID, archiver.CommitID,

View File

@ -9,7 +9,6 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/contexttest"
_ "code.gitea.io/gitea/models/actions" _ "code.gitea.io/gitea/models/actions"
@ -31,47 +30,47 @@ func TestArchive_Basic(t *testing.T) {
contexttest.LoadGitRepo(t, ctx) contexttest.LoadGitRepo(t, ctx)
defer ctx.Repo.GitRepo.Close() defer ctx.Repo.GitRepo.Close()
bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, bogusReq) assert.NotNil(t, bogusReq)
assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName()) assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName())
// Check a series of bogus requests. // Check a series of bogus requests.
// Step 1, valid commit with a bad extension. // Step 1, valid commit with a bad extension.
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, 100) bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".unknown")
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, bogusReq) assert.Nil(t, bogusReq)
// Step 2, missing commit. // Step 2, missing commit.
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff", git.ZIP) bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip")
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, bogusReq) assert.Nil(t, bogusReq)
// Step 3, doesn't look like branch/tag/commit. // Step 3, doesn't look like branch/tag/commit.
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db", git.ZIP) bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip")
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, bogusReq) assert.Nil(t, bogusReq)
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master", git.ZIP) bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, bogusReq) assert.NotNil(t, bogusReq)
assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName()) assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName())
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive", git.ZIP) bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, bogusReq) assert.NotNil(t, bogusReq)
assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName()) assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName())
// Now two valid requests, firstCommit with valid extensions. // Now two valid requests, firstCommit with valid extensions.
zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, zipReq) assert.NotNil(t, zipReq)
tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.TARGZ) tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, tgzReq) assert.NotNil(t, tgzReq)
secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.ZIP) secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".bundle")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, secondReq) assert.NotNil(t, secondReq)
@ -91,7 +90,7 @@ func TestArchive_Basic(t *testing.T) {
// Sleep two seconds to make sure the queue doesn't change. // Sleep two seconds to make sure the queue doesn't change.
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err) assert.NoError(t, err)
// This zipReq should match what's sitting in the queue, as we haven't // This zipReq should match what's sitting in the queue, as we haven't
// let it release yet. From the consumer's point of view, this looks like // let it release yet. From the consumer's point of view, this looks like
@ -106,12 +105,12 @@ func TestArchive_Basic(t *testing.T) {
// Now we'll submit a request and TimedWaitForCompletion twice, before and // Now we'll submit a request and TimedWaitForCompletion twice, before and
// after we release it. We should trigger both the timeout and non-timeout // after we release it. We should trigger both the timeout and non-timeout
// cases. // cases.
timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.TARGZ) timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, timedReq) assert.NotNil(t, timedReq)
doArchive(db.DefaultContext, timedReq) doArchive(db.DefaultContext, timedReq)
zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err) assert.NoError(t, err)
// Now, we're guaranteed to have released the original zipReq from the queue. // Now, we're guaranteed to have released the original zipReq from the queue.
// Ensure that we don't get handed back the released entry somehow, but they // Ensure that we don't get handed back the released entry somehow, but they
@ -129,6 +128,6 @@ func TestArchive_Basic(t *testing.T) {
} }
func TestErrUnknownArchiveFormat(t *testing.T) { func TestErrUnknownArchiveFormat(t *testing.T) {
err := ErrUnknownArchiveFormat{RequestFormat: "master"} err := ErrUnknownArchiveFormat{RequestNameType: "xxx"}
assert.ErrorIs(t, err, ErrUnknownArchiveFormat{}) assert.ErrorIs(t, err, ErrUnknownArchiveFormat{})
} }

View File

@ -763,12 +763,10 @@ func (m *webhookNotifier) PullRequestReviewRequest(ctx context.Context, doer *us
func (m *webhookNotifier) CreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) { func (m *webhookNotifier) CreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
apiPusher := convert.ToUser(ctx, pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil)
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}) apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone})
refName := refFullName.ShortName()
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventCreate, &api.CreatePayload{ if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventCreate, &api.CreatePayload{
Ref: refName, // FIXME: should it be a full ref name? Ref: refFullName.ShortName(), // FIXME: should it be a full ref name? But it will break the existing webhooks?
Sha: refID, Sha: refID,
RefType: refFullName.RefType(), RefType: string(refFullName.RefType()),
Repo: apiRepo, Repo: apiRepo,
Sender: apiPusher, Sender: apiPusher,
}); err != nil { }); err != nil {
@ -800,11 +798,9 @@ func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *use
func (m *webhookNotifier) DeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) { func (m *webhookNotifier) DeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
apiPusher := convert.ToUser(ctx, pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil)
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}) apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner})
refName := refFullName.ShortName()
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventDelete, &api.DeletePayload{ if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventDelete, &api.DeletePayload{
Ref: refName, // FIXME: should it be a full ref name? Ref: refFullName.ShortName(), // FIXME: should it be a full ref name? But it will break the existing webhooks?
RefType: refFullName.RefType(), RefType: string(refFullName.RefType()),
PusherType: api.PusherTypeUser, PusherType: api.PusherTypeUser,
Repo: apiRepo, Repo: apiRepo,
Sender: apiPusher, Sender: apiPusher,

View File

@ -84,9 +84,9 @@ func SlackLinkFormatter(url, text string) string {
// SlackLinkToRef slack-formatter link to a repo ref // SlackLinkToRef slack-formatter link to a repo ref
func SlackLinkToRef(repoURL, ref string) string { func SlackLinkToRef(repoURL, ref string) string {
// FIXME: SHA1 hardcoded here // FIXME: SHA1 hardcoded here
url := git.RefURL(repoURL, ref) refName := git.RefName(ref)
refName := git.RefName(ref).ShortName() url := repoURL + "/src/" + refName.RefWebLinkPath()
return SlackLinkFormatter(url, refName) return SlackLinkFormatter(url, refName.ShortName())
} }
// Create implements payloadConvertor Create method // Create implements payloadConvertor Create method

View File

@ -42,7 +42,7 @@
</button> </button>
{{end}} {{end}}
{{if .EnableFeed}} {{if .EnableFeed}}
<a role="button" class="btn interact-bg tw-p-2" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a> <a role="button" class="btn interact-bg tw-p-2" href="{{$.RepoLink}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
{{end}} {{end}}
{{if not $.DisableDownloadSourceArchives}} {{if not $.DisableDownloadSourceArchives}}
<div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" ($.DefaultBranchBranch.DBBranch.Name)}}"> <div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" ($.DefaultBranchBranch.DBBranch.Name)}}">
@ -162,7 +162,7 @@
</button> </button>
{{end}} {{end}}
{{if $.EnableFeed}} {{if $.EnableFeed}}
<a role="button" class="btn interact-bg tw-p-2" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a> <a role="button" class="btn interact-bg tw-p-2" href="{{$.RepoLink}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
{{end}} {{end}}
{{if and (not .DBBranch.IsDeleted) (not $.DisableDownloadSourceArchives)}} {{if and (not .DBBranch.IsDeleted) (not $.DisableDownloadSourceArchives)}}
<div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" (.DBBranch.Name)}}"> <div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" (.DBBranch.Name)}}">

View File

@ -32,12 +32,14 @@
{{end}} {{end}}
</div> </div>
{{if and (not $.DisableDownloadSourceArchives) $.RefName}} {{if and (not $.DisableDownloadSourceArchives) $.RefFullName}}
<div class="divider"></div> <div class="divider"></div>
<div class="flex-items-block clone-panel-list"> <div class="flex-items-block clone-panel-list">
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a> {{/* FIXME: here it only uses the shortname in the ref to build the link, it can't distinguish the branch/tag/commit with the same name
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a> in the future, it's better to use something like "/archive/branch/the-name.zip", "/archive/tag/the-name.zip" */}}
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a> <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a>
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a>
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a>
</div> </div>
{{end}} {{end}}
{{end}} {{end}}

View File

@ -57,7 +57,7 @@
<a download class="btn-octicon" data-tooltip-content="{{ctx.Locale.Tr "repo.download_file"}}" href="{{$.RawFileLink}}">{{svg "octicon-download"}}</a> <a download class="btn-octicon" data-tooltip-content="{{ctx.Locale.Tr "repo.download_file"}}" href="{{$.RawFileLink}}">{{svg "octicon-download"}}</a>
<a id="copy-content" class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}"{{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{ctx.Locale.Tr "copy_content"}}{{else}}{{ctx.Locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy"}}</a> <a id="copy-content" class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}"{{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{ctx.Locale.Tr "copy_content"}}{{else}}{{ctx.Locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy"}}</a>
{{if .EnableFeed}} {{if .EnableFeed}}
<a class="btn-octicon" href="{{$.FeedURL}}/rss/{{$.RefTypeNameSubURL}}/{{PathEscapeSegments .TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}"> <a class="btn-octicon" href="{{$.RepoLink}}/rss/{{$.RefTypeNameSubURL}}/{{PathEscapeSegments .TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
{{svg "octicon-rss"}} {{svg "octicon-rss"}}
</a> </a>
{{end}} {{end}}

View File

@ -60,7 +60,9 @@ func TestEmptyRepoAddFile(t *testing.T) {
session := loginUser(t, "user30") session := loginUser(t, "user30")
req := NewRequest(t, "GET", "/user30/empty") req := NewRequest(t, "GET", "/user30/empty")
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "empty-repo-guide") bodyString := resp.Body.String()
assert.Contains(t, bodyString, "empty-repo-guide")
assert.True(t, test.IsNormalPageCompleted(bodyString))
req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch) req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
resp = session.MakeRequest(t, req, http.StatusOK) resp = session.MakeRequest(t, req, http.StatusOK)

View File

@ -38,9 +38,6 @@ export function initRepoEditor() {
const dropzoneUpload = document.querySelector<HTMLElement>('.page-content.repository.editor.upload .dropzone'); const dropzoneUpload = document.querySelector<HTMLElement>('.page-content.repository.editor.upload .dropzone');
if (dropzoneUpload) initDropzone(dropzoneUpload); if (dropzoneUpload) initDropzone(dropzoneUpload);
const editArea = document.querySelector<HTMLTextAreaElement>('.page-content.repository.editor textarea#edit_area');
if (!editArea) return;
for (const el of queryElems<HTMLInputElement>(document, '.js-quick-pull-choice-option')) { for (const el of queryElems<HTMLInputElement>(document, '.js-quick-pull-choice-option')) {
el.addEventListener('input', () => { el.addEventListener('input', () => {
if (el.value === 'commit-to-new-branch') { if (el.value === 'commit-to-new-branch') {
@ -55,6 +52,7 @@ export function initRepoEditor() {
} }
const filenameInput = document.querySelector<HTMLInputElement>('#file-name'); const filenameInput = document.querySelector<HTMLInputElement>('#file-name');
if (!filenameInput) return;
function joinTreePath() { function joinTreePath() {
const parts = []; const parts = [];
for (const el of document.querySelectorAll('.breadcrumb span.section')) { for (const el of document.querySelectorAll('.breadcrumb span.section')) {
@ -144,6 +142,10 @@ export function initRepoEditor() {
} }
}); });
// on the upload page, there is no editor(textarea)
const editArea = document.querySelector<HTMLTextAreaElement>('.page-content.repository.editor textarea#edit_area');
if (!editArea) return;
const elForm = document.querySelector<HTMLFormElement>('.repository.editor .edit.form'); const elForm = document.querySelector<HTMLFormElement>('.repository.editor .edit.form');
initEditPreviewTab(elForm); initEditPreviewTab(elForm);