mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-17 00:01:00 -04:00
Compare commits
11 Commits
f0b773e0ce
...
254a82842a
Author | SHA1 | Date | |
---|---|---|---|
|
254a82842a | ||
|
9fd63aaad1 | ||
|
cf2356062f | ||
|
e882398c5a | ||
|
fdf71460f9 | ||
|
64f2d70262 | ||
|
72b3af74be | ||
|
6e19484f4d | ||
|
5a871932f0 | ||
|
c76b221cca | ||
|
c6f1fb1c6d |
8
.github/workflows/files-changed.yml
vendored
8
.github/workflows/files-changed.yml
vendored
@ -15,6 +15,8 @@ on:
|
|||||||
value: ${{ jobs.detect.outputs.templates }}
|
value: ${{ jobs.detect.outputs.templates }}
|
||||||
docker:
|
docker:
|
||||||
value: ${{ jobs.detect.outputs.docker }}
|
value: ${{ jobs.detect.outputs.docker }}
|
||||||
|
swagger:
|
||||||
|
value: ${{ jobs.detect.outputs.swagger }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
detect:
|
detect:
|
||||||
@ -27,6 +29,7 @@ jobs:
|
|||||||
actions: ${{ steps.changes.outputs.actions }}
|
actions: ${{ steps.changes.outputs.actions }}
|
||||||
templates: ${{ steps.changes.outputs.templates }}
|
templates: ${{ steps.changes.outputs.templates }}
|
||||||
docker: ${{ steps.changes.outputs.docker }}
|
docker: ${{ steps.changes.outputs.docker }}
|
||||||
|
swagger: ${{ steps.changes.outputs.swagger }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
@ -36,6 +39,7 @@ jobs:
|
|||||||
backend:
|
backend:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "templates/**/*.tmpl"
|
- "templates/**/*.tmpl"
|
||||||
|
- "assets/emoji.json"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
- "Makefile"
|
- "Makefile"
|
||||||
@ -43,6 +47,7 @@ jobs:
|
|||||||
frontend:
|
frontend:
|
||||||
- "**/*.js"
|
- "**/*.js"
|
||||||
- "web_src/**"
|
- "web_src/**"
|
||||||
|
- "assets/emoji.json"
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "package-lock.json"
|
- "package-lock.json"
|
||||||
- "Makefile"
|
- "Makefile"
|
||||||
@ -63,3 +68,6 @@ jobs:
|
|||||||
- "Dockerfile.rootless"
|
- "Dockerfile.rootless"
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
- "Makefile"
|
- "Makefile"
|
||||||
|
|
||||||
|
swagger:
|
||||||
|
- "templates/swagger/v1_json.tmpl"
|
||||||
|
12
.github/workflows/pull-compliance.yml
vendored
12
.github/workflows/pull-compliance.yml
vendored
@ -39,6 +39,18 @@ jobs:
|
|||||||
- run: make deps-py
|
- run: make deps-py
|
||||||
- run: make lint-templates
|
- run: make lint-templates
|
||||||
|
|
||||||
|
lint-swagger:
|
||||||
|
if: needs.files-changed.outputs.swagger == 'true'
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- run: make deps-frontend
|
||||||
|
- run: make lint-swagger
|
||||||
|
|
||||||
lint-go-windows:
|
lint-go-windows:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
|
4
Makefile
4
Makefile
@ -360,10 +360,10 @@ lint: lint-frontend lint-backend
|
|||||||
lint-fix: lint-frontend-fix lint-backend-fix
|
lint-fix: lint-frontend-fix lint-backend-fix
|
||||||
|
|
||||||
.PHONY: lint-frontend
|
.PHONY: lint-frontend
|
||||||
lint-frontend: lint-js lint-css lint-md lint-swagger
|
lint-frontend: lint-js lint-css
|
||||||
|
|
||||||
.PHONY: lint-frontend-fix
|
.PHONY: lint-frontend-fix
|
||||||
lint-frontend-fix: lint-js-fix lint-css-fix lint-md lint-swagger
|
lint-frontend-fix: lint-js-fix lint-css-fix
|
||||||
|
|
||||||
.PHONY: lint-backend
|
.PHONY: lint-backend
|
||||||
lint-backend: lint-go lint-go-vet lint-editorconfig
|
lint-backend: lint-go lint-go-vet lint-editorconfig
|
||||||
|
2
assets/emoji.json
generated
2
assets/emoji.json
generated
File diff suppressed because one or more lines are too long
@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
||||||
maxUnicodeVersion = 14
|
maxUnicodeVersion = 15
|
||||||
)
|
)
|
||||||
|
|
||||||
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||||
|
@ -164,3 +164,23 @@ Although we would like to provide more options, our limited manpower means that
|
|||||||
However, both Gitea and act runner are completely open source, so anyone can create a new/better implementation.
|
However, both Gitea and act runner are completely open source, so anyone can create a new/better implementation.
|
||||||
We support your choice, no matter how you decide.
|
We support your choice, no matter how you decide.
|
||||||
In case you fork act runner to create your own version: Please contribute the changes back if you can and if you think your changes will help others as well.
|
In case you fork act runner to create your own version: Please contribute the changes back if you can and if you think your changes will help others as well.
|
||||||
|
|
||||||
|
## What workflow trigger events does Gitea support?
|
||||||
|
|
||||||
|
All events listed in this table are supported events and are compatible with GitHub.
|
||||||
|
For events supported only by GitHub, see GitHub's [documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows).
|
||||||
|
|
||||||
|
| trigger event | activity types |
|
||||||
|
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| create | not applicable |
|
||||||
|
| delete | not applicable |
|
||||||
|
| fork | not applicable |
|
||||||
|
| gollum | not applicable |
|
||||||
|
| push | not applicable |
|
||||||
|
| issues | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `milestoned`, `demilestoned`, `labeled`, `unlabeled` |
|
||||||
|
| issue_comment | `created`, `edited`, `deleted` |
|
||||||
|
| pull_request | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `synchronize`, `labeled`, `unlabeled` |
|
||||||
|
| pull_request_review | `submitted`, `edited` |
|
||||||
|
| pull_request_review_comment | `created`, `edited` |
|
||||||
|
| release | `published`, `edited` |
|
||||||
|
| registry_package | `published` |
|
||||||
|
@ -164,3 +164,23 @@ defaults:
|
|||||||
然而,无论您如何决定,Gitea 和act runner都是完全开源的,所以任何人都可以创建一个新的/更好的实现。
|
然而,无论您如何决定,Gitea 和act runner都是完全开源的,所以任何人都可以创建一个新的/更好的实现。
|
||||||
我们支持您的选择,无论您如何决定。
|
我们支持您的选择,无论您如何决定。
|
||||||
如果您选择分支act runner来创建自己的版本,请在您认为您的更改对其他人也有帮助的情况下贡献这些更改。
|
如果您选择分支act runner来创建自己的版本,请在您认为您的更改对其他人也有帮助的情况下贡献这些更改。
|
||||||
|
|
||||||
|
## Gitea 支持哪些工作流触发事件?
|
||||||
|
|
||||||
|
表格中列出的所有事件都是支持的,并且与 GitHub 兼容。
|
||||||
|
对于仅 GitHub 支持的事件,请参阅 GitHub 的[文档](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)。
|
||||||
|
|
||||||
|
| 触发事件 | 活动类型 |
|
||||||
|
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| create | 不适用 |
|
||||||
|
| delete | 不适用 |
|
||||||
|
| fork | 不适用 |
|
||||||
|
| gollum | 不适用 |
|
||||||
|
| push | 不适用 |
|
||||||
|
| issues | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `milestoned`, `demilestoned`, `labeled`, `unlabeled` |
|
||||||
|
| issue_comment | `created`, `edited`, `deleted` |
|
||||||
|
| pull_request | `opened`, `edited`, `closed`, `reopened`, `assigned`, `unassigned`, `synchronize`, `labeled`, `unlabeled` |
|
||||||
|
| pull_request_review | `submitted`, `edited` |
|
||||||
|
| pull_request_review_comment | `created`, `edited` |
|
||||||
|
| release | `published`, `edited` |
|
||||||
|
| registry_package | `published` |
|
||||||
|
@ -318,90 +318,6 @@ func (err ErrFilePathProtected) Unwrap() error {
|
|||||||
return util.ErrPermissionDenied
|
return util.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// __________ .__
|
|
||||||
// \______ \____________ ____ ____ | |__
|
|
||||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
|
||||||
// | | \ | | \// __ \| | \ \___| Y \
|
|
||||||
// |______ / |__| (____ /___| /\___ >___| /
|
|
||||||
// \/ \/ \/ \/ \/
|
|
||||||
|
|
||||||
// ErrBranchDoesNotExist represents an error that branch with such name does not exist.
|
|
||||||
type ErrBranchDoesNotExist struct {
|
|
||||||
BranchName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrBranchDoesNotExist checks if an error is an ErrBranchDoesNotExist.
|
|
||||||
func IsErrBranchDoesNotExist(err error) bool {
|
|
||||||
_, ok := err.(ErrBranchDoesNotExist)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchDoesNotExist) Error() string {
|
|
||||||
return fmt.Sprintf("branch does not exist [name: %s]", err.BranchName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchDoesNotExist) Unwrap() error {
|
|
||||||
return util.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBranchAlreadyExists represents an error that branch with such name already exists.
|
|
||||||
type ErrBranchAlreadyExists struct {
|
|
||||||
BranchName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrBranchAlreadyExists checks if an error is an ErrBranchAlreadyExists.
|
|
||||||
func IsErrBranchAlreadyExists(err error) bool {
|
|
||||||
_, ok := err.(ErrBranchAlreadyExists)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchAlreadyExists) Error() string {
|
|
||||||
return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchAlreadyExists) Unwrap() error {
|
|
||||||
return util.ErrAlreadyExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
|
|
||||||
type ErrBranchNameConflict struct {
|
|
||||||
BranchName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrBranchNameConflict checks if an error is an ErrBranchNameConflict.
|
|
||||||
func IsErrBranchNameConflict(err error) bool {
|
|
||||||
_, ok := err.(ErrBranchNameConflict)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchNameConflict) Error() string {
|
|
||||||
return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchNameConflict) Unwrap() error {
|
|
||||||
return util.ErrAlreadyExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBranchesEqual represents an error that branch name conflicts with other branch.
|
|
||||||
type ErrBranchesEqual struct {
|
|
||||||
BaseBranchName string
|
|
||||||
HeadBranchName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrBranchesEqual checks if an error is an ErrBranchesEqual.
|
|
||||||
func IsErrBranchesEqual(err error) bool {
|
|
||||||
_, ok := err.(ErrBranchesEqual)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchesEqual) Error() string {
|
|
||||||
return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBranchesEqual) Unwrap() error {
|
|
||||||
return util.ErrInvalidArgument
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
||||||
type ErrDisallowedToMerge struct {
|
type ErrDisallowedToMerge struct {
|
||||||
Reason string
|
Reason string
|
||||||
|
47
models/fixtures/branch.yml
Normal file
47
models/fixtures/branch.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
-
|
||||||
|
id: 1
|
||||||
|
repo_id: 1
|
||||||
|
name: 'foo'
|
||||||
|
commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
|
||||||
|
commit_message: 'first commit'
|
||||||
|
commit_time: 978307100
|
||||||
|
pusher_id: 1
|
||||||
|
is_deleted: true
|
||||||
|
deleted_by_id: 1
|
||||||
|
deleted_unix: 978307200
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
repo_id: 1
|
||||||
|
name: 'bar'
|
||||||
|
commit_id: '62fb502a7172d4453f0322a2cc85bddffa57f07a'
|
||||||
|
commit_message: 'second commit'
|
||||||
|
commit_time: 978307100
|
||||||
|
pusher_id: 1
|
||||||
|
is_deleted: true
|
||||||
|
deleted_by_id: 99
|
||||||
|
deleted_unix: 978307200
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 3
|
||||||
|
repo_id: 1
|
||||||
|
name: 'branch2'
|
||||||
|
commit_id: '985f0301dba5e7b34be866819cd15ad3d8f508ee'
|
||||||
|
commit_message: 'make pull5 outdated'
|
||||||
|
commit_time: 1579166279
|
||||||
|
pusher_id: 1
|
||||||
|
is_deleted: false
|
||||||
|
deleted_by_id: 0
|
||||||
|
deleted_unix: 0
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 4
|
||||||
|
repo_id: 1
|
||||||
|
name: 'master'
|
||||||
|
commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
|
||||||
|
commit_message: 'Initial commit'
|
||||||
|
commit_time: 1489927679
|
||||||
|
pusher_id: 1
|
||||||
|
is_deleted: false
|
||||||
|
deleted_by_id: 0
|
||||||
|
deleted_unix: 0
|
@ -1,15 +0,0 @@
|
|||||||
-
|
|
||||||
id: 1
|
|
||||||
repo_id: 1
|
|
||||||
name: foo
|
|
||||||
commit: 1213212312313213213132131
|
|
||||||
deleted_by_id: 1
|
|
||||||
deleted_unix: 978307200
|
|
||||||
|
|
||||||
-
|
|
||||||
id: 2
|
|
||||||
repo_id: 1
|
|
||||||
name: bar
|
|
||||||
commit: 5655464564554545466464655
|
|
||||||
deleted_by_id: 99
|
|
||||||
deleted_unix: 978307200
|
|
379
models/git/branch.go
Normal file
379
models/git/branch.go
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrBranchNotExist represents an error that branch with such name does not exist.
|
||||||
|
type ErrBranchNotExist struct {
|
||||||
|
RepoID int64
|
||||||
|
BranchName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrBranchNotExist checks if an error is an ErrBranchDoesNotExist.
|
||||||
|
func IsErrBranchNotExist(err error) bool {
|
||||||
|
_, ok := err.(ErrBranchNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("branch does not exist [repo_id: %d name: %s]", err.RepoID, err.BranchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchNotExist) Unwrap() error {
|
||||||
|
return util.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBranchAlreadyExists represents an error that branch with such name already exists.
|
||||||
|
type ErrBranchAlreadyExists struct {
|
||||||
|
BranchName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrBranchAlreadyExists checks if an error is an ErrBranchAlreadyExists.
|
||||||
|
func IsErrBranchAlreadyExists(err error) bool {
|
||||||
|
_, ok := err.(ErrBranchAlreadyExists)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchAlreadyExists) Error() string {
|
||||||
|
return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchAlreadyExists) Unwrap() error {
|
||||||
|
return util.ErrAlreadyExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
|
||||||
|
type ErrBranchNameConflict struct {
|
||||||
|
BranchName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrBranchNameConflict checks if an error is an ErrBranchNameConflict.
|
||||||
|
func IsErrBranchNameConflict(err error) bool {
|
||||||
|
_, ok := err.(ErrBranchNameConflict)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchNameConflict) Error() string {
|
||||||
|
return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchNameConflict) Unwrap() error {
|
||||||
|
return util.ErrAlreadyExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBranchesEqual represents an error that base branch is equal to the head branch.
|
||||||
|
type ErrBranchesEqual struct {
|
||||||
|
BaseBranchName string
|
||||||
|
HeadBranchName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrBranchesEqual checks if an error is an ErrBranchesEqual.
|
||||||
|
func IsErrBranchesEqual(err error) bool {
|
||||||
|
_, ok := err.(ErrBranchesEqual)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchesEqual) Error() string {
|
||||||
|
return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBranchesEqual) Unwrap() error {
|
||||||
|
return util.ErrInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
// Branch represents a branch of a repository
|
||||||
|
// For those repository who have many branches, stored into database is a good choice
|
||||||
|
// for pagination, keyword search and filtering
|
||||||
|
type Branch struct {
|
||||||
|
ID int64
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||||
|
CommitID string
|
||||||
|
CommitMessage string `xorm:"TEXT"`
|
||||||
|
PusherID int64
|
||||||
|
Pusher *user_model.User `xorm:"-"`
|
||||||
|
IsDeleted bool `xorm:"index"`
|
||||||
|
DeletedByID int64
|
||||||
|
DeletedBy *user_model.User `xorm:"-"`
|
||||||
|
DeletedUnix timeutil.TimeStamp `xorm:"index"`
|
||||||
|
CommitTime timeutil.TimeStamp // The commit
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Branch) LoadDeletedBy(ctx context.Context) (err error) {
|
||||||
|
if b.DeletedBy == nil {
|
||||||
|
b.DeletedBy, err = user_model.GetUserByID(ctx, b.DeletedByID)
|
||||||
|
if user_model.IsErrUserNotExist(err) {
|
||||||
|
b.DeletedBy = user_model.NewGhostUser()
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Branch) LoadPusher(ctx context.Context) (err error) {
|
||||||
|
if b.Pusher == nil && b.PusherID > 0 {
|
||||||
|
b.Pusher, err = user_model.GetUserByID(ctx, b.PusherID)
|
||||||
|
if user_model.IsErrUserNotExist(err) {
|
||||||
|
b.Pusher = user_model.NewGhostUser()
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
db.RegisterModel(new(Branch))
|
||||||
|
db.RegisterModel(new(RenamedBranch))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, error) {
|
||||||
|
var branch Branch
|
||||||
|
has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("name=?", branchName).Get(&branch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrBranchNotExist{
|
||||||
|
RepoID: repoID,
|
||||||
|
BranchName: branchName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &branch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddBranches(ctx context.Context, branches []*Branch) error {
|
||||||
|
for _, branch := range branches {
|
||||||
|
if _, err := db.GetEngine(ctx).Insert(branch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDeletedBranchByID(ctx context.Context, repoID, branchID int64) (*Branch, error) {
|
||||||
|
var branch Branch
|
||||||
|
has, err := db.GetEngine(ctx).ID(branchID).Get(&branch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrBranchNotExist{
|
||||||
|
RepoID: repoID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if branch.RepoID != repoID {
|
||||||
|
return nil, ErrBranchNotExist{
|
||||||
|
RepoID: repoID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !branch.IsDeleted {
|
||||||
|
return nil, ErrBranchNotExist{
|
||||||
|
RepoID: repoID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &branch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64) error {
|
||||||
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
branches := make([]*Branch, 0, len(branchIDs))
|
||||||
|
if err := db.GetEngine(ctx).In("id", branchIDs).Find(&branches); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, branch := range branches {
|
||||||
|
if err := AddDeletedBranch(ctx, repoID, branch.Name, doerID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information
|
||||||
|
// If it doest not exist, insert a new record into database
|
||||||
|
func UpdateBranch(ctx context.Context, repoID int64, branchName, commitID, commitMessage string, pusherID int64, commitTime time.Time) error {
|
||||||
|
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
|
||||||
|
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix").
|
||||||
|
Update(&Branch{
|
||||||
|
CommitID: commitID,
|
||||||
|
CommitMessage: commitMessage,
|
||||||
|
PusherID: pusherID,
|
||||||
|
CommitTime: timeutil.TimeStamp(commitTime.Unix()),
|
||||||
|
IsDeleted: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cnt > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.Insert(ctx, &Branch{
|
||||||
|
RepoID: repoID,
|
||||||
|
Name: branchName,
|
||||||
|
CommitID: commitID,
|
||||||
|
CommitMessage: commitMessage,
|
||||||
|
PusherID: pusherID,
|
||||||
|
CommitTime: timeutil.TimeStamp(commitTime.Unix()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDeletedBranch adds a deleted branch to the database
|
||||||
|
func AddDeletedBranch(ctx context.Context, repoID int64, branchName string, deletedByID int64) error {
|
||||||
|
branch, err := GetBranch(ctx, repoID, branchName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if branch.IsDeleted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=? AND is_deleted=?", repoID, branchName, false).
|
||||||
|
Cols("is_deleted, deleted_by_id, deleted_unix").
|
||||||
|
Update(&Branch{
|
||||||
|
IsDeleted: true,
|
||||||
|
DeletedByID: deletedByID,
|
||||||
|
DeletedUnix: timeutil.TimeStampNow(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cnt == 0 {
|
||||||
|
return fmt.Errorf("branch %s not found or has been deleted", branchName)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveDeletedBranchByID(ctx context.Context, repoID, branchID int64) error {
|
||||||
|
_, err := db.GetEngine(ctx).Where("repo_id=? AND id=? AND is_deleted = ?", repoID, branchID, true).Delete(new(Branch))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldDeletedBranches removes old deleted branches
|
||||||
|
func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
|
||||||
|
// Nothing to do for shutdown or terminate
|
||||||
|
log.Trace("Doing: DeletedBranchesCleanup")
|
||||||
|
|
||||||
|
deleteBefore := time.Now().Add(-olderThan)
|
||||||
|
_, err := db.GetEngine(ctx).Where("is_deleted=? AND deleted_unix < ?", true, deleteBefore.Unix()).Delete(new(Branch))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("DeletedBranchesCleanup: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenamedBranch provide renamed branch log
|
||||||
|
// will check it when a branch can't be found
|
||||||
|
type RenamedBranch struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"INDEX NOT NULL"`
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindRenamedBranch check if a branch was renamed
|
||||||
|
func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
|
||||||
|
branch = &RenamedBranch{
|
||||||
|
RepoID: repoID,
|
||||||
|
From: from,
|
||||||
|
}
|
||||||
|
exist, err = db.GetEngine(ctx).Get(branch)
|
||||||
|
|
||||||
|
return branch, exist, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenameBranch rename a branch
|
||||||
|
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
|
||||||
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
// 1. update branch in database
|
||||||
|
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
|
||||||
|
Name: to,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
} else if n <= 0 {
|
||||||
|
return ErrBranchNotExist{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
BranchName: from,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. update default branch if needed
|
||||||
|
isDefault := repo.DefaultBranch == from
|
||||||
|
if isDefault {
|
||||||
|
repo.DefaultBranch = to
|
||||||
|
_, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Update protected branch if needed
|
||||||
|
protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if protectedBranch != nil {
|
||||||
|
// there is a protect rule for this branch
|
||||||
|
protectedBranch.RuleName = to
|
||||||
|
_, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// some glob protect rules may match this branch
|
||||||
|
protected, err := IsBranchProtected(ctx, repo.ID, from)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if protected {
|
||||||
|
return ErrBranchIsProtected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Update all not merged pull request base branch name
|
||||||
|
_, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
|
||||||
|
repo.ID, from, false).
|
||||||
|
Update(map[string]interface{}{"base_branch": to})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. do git action
|
||||||
|
if err = gitAction(isDefault); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. insert renamed branch record
|
||||||
|
renamedBranch := &RenamedBranch{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
err = db.Insert(ctx, renamedBranch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return committer.Commit()
|
||||||
|
}
|
132
models/git/branch_list.go
Normal file
132
models/git/branch_list.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BranchList []*Branch
|
||||||
|
|
||||||
|
func (branches BranchList) LoadDeletedBy(ctx context.Context) error {
|
||||||
|
ids := container.Set[int64]{}
|
||||||
|
for _, branch := range branches {
|
||||||
|
if !branch.IsDeleted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ids.Add(branch.DeletedByID)
|
||||||
|
}
|
||||||
|
usersMap := make(map[int64]*user_model.User, len(ids))
|
||||||
|
if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, branch := range branches {
|
||||||
|
if !branch.IsDeleted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
branch.DeletedBy = usersMap[branch.DeletedByID]
|
||||||
|
if branch.DeletedBy == nil {
|
||||||
|
branch.DeletedBy = user_model.NewGhostUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (branches BranchList) LoadPusher(ctx context.Context) error {
|
||||||
|
ids := container.Set[int64]{}
|
||||||
|
for _, branch := range branches {
|
||||||
|
if branch.PusherID > 0 { // pusher_id maybe zero because some branches are sync by backend with no pusher
|
||||||
|
ids.Add(branch.PusherID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usersMap := make(map[int64]*user_model.User, len(ids))
|
||||||
|
if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, branch := range branches {
|
||||||
|
if branch.PusherID <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
branch.Pusher = usersMap[branch.PusherID]
|
||||||
|
if branch.Pusher == nil {
|
||||||
|
branch.Pusher = user_model.NewGhostUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
BranchOrderByNameAsc = "name ASC"
|
||||||
|
BranchOrderByCommitTimeDesc = "commit_time DESC"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FindBranchOptions struct {
|
||||||
|
db.ListOptions
|
||||||
|
RepoID int64
|
||||||
|
ExcludeBranchNames []string
|
||||||
|
IsDeletedBranch util.OptionalBool
|
||||||
|
OrderBy string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *FindBranchOptions) Cond() builder.Cond {
|
||||||
|
cond := builder.NewCond()
|
||||||
|
if opts.RepoID > 0 {
|
||||||
|
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.ExcludeBranchNames) > 0 {
|
||||||
|
cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames))
|
||||||
|
}
|
||||||
|
if !opts.IsDeletedBranch.IsNone() {
|
||||||
|
cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.IsTrue()})
|
||||||
|
}
|
||||||
|
return cond
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountBranches(ctx context.Context, opts FindBranchOptions) (int64, error) {
|
||||||
|
return db.GetEngine(ctx).Where(opts.Cond()).Count(&Branch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
|
||||||
|
if !opts.IsDeletedBranch.IsFalse() { // if deleted branch included, put them at the end
|
||||||
|
sess = sess.OrderBy("is_deleted ASC")
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.OrderBy == "" {
|
||||||
|
opts.OrderBy = BranchOrderByCommitTimeDesc
|
||||||
|
}
|
||||||
|
return sess.OrderBy(opts.OrderBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, error) {
|
||||||
|
sess := db.GetEngine(ctx).Where(opts.Cond())
|
||||||
|
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||||
|
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||||
|
}
|
||||||
|
sess = orderByBranches(sess, opts)
|
||||||
|
|
||||||
|
var branches []*Branch
|
||||||
|
return branches, sess.Find(&branches)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
|
||||||
|
sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
|
||||||
|
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||||
|
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||||
|
}
|
||||||
|
sess = orderByBranches(sess, opts)
|
||||||
|
var branches []string
|
||||||
|
if err := sess.Table("branch").Find(&branches); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return branches, nil
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -18,24 +19,37 @@ import (
|
|||||||
func TestAddDeletedBranch(t *testing.T) {
|
func TestAddDeletedBranch(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||||
|
|
||||||
assert.Error(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
|
assert.True(t, firstBranch.IsDeleted)
|
||||||
assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, "test", "5655464564554545466464656", int64(1)))
|
assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, firstBranch.Name, firstBranch.DeletedByID))
|
||||||
|
assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, "branch2", int64(1)))
|
||||||
|
|
||||||
|
secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo.ID, Name: "branch2"})
|
||||||
|
assert.True(t, secondBranch.IsDeleted)
|
||||||
|
|
||||||
|
err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.Name, secondBranch.CommitID, secondBranch.CommitMessage, secondBranch.PusherID, secondBranch.CommitTime.AsLocalTime())
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDeletedBranches(t *testing.T) {
|
func TestGetDeletedBranches(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
|
||||||
branches, err := git_model.GetDeletedBranches(db.DefaultContext, repo.ID)
|
branches, err := git_model.FindBranches(db.DefaultContext, git_model.FindBranchOptions{
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
RepoID: repo.ID,
|
||||||
|
IsDeletedBranch: util.OptionalBoolTrue,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, branches, 2)
|
assert.Len(t, branches, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDeletedBranch(t *testing.T) {
|
func TestGetDeletedBranch(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||||
|
|
||||||
assert.NotNil(t, getDeletedBranch(t, firstBranch))
|
assert.NotNil(t, getDeletedBranch(t, firstBranch))
|
||||||
}
|
}
|
||||||
@ -43,18 +57,18 @@ func TestGetDeletedBranch(t *testing.T) {
|
|||||||
func TestDeletedBranchLoadUser(t *testing.T) {
|
func TestDeletedBranchLoadUser(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||||
secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
|
secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 2})
|
||||||
|
|
||||||
branch := getDeletedBranch(t, firstBranch)
|
branch := getDeletedBranch(t, firstBranch)
|
||||||
assert.Nil(t, branch.DeletedBy)
|
assert.Nil(t, branch.DeletedBy)
|
||||||
branch.LoadUser(db.DefaultContext)
|
branch.LoadDeletedBy(db.DefaultContext)
|
||||||
assert.NotNil(t, branch.DeletedBy)
|
assert.NotNil(t, branch.DeletedBy)
|
||||||
assert.Equal(t, "user1", branch.DeletedBy.Name)
|
assert.Equal(t, "user1", branch.DeletedBy.Name)
|
||||||
|
|
||||||
branch = getDeletedBranch(t, secondBranch)
|
branch = getDeletedBranch(t, secondBranch)
|
||||||
assert.Nil(t, branch.DeletedBy)
|
assert.Nil(t, branch.DeletedBy)
|
||||||
branch.LoadUser(db.DefaultContext)
|
branch.LoadDeletedBy(db.DefaultContext)
|
||||||
assert.NotNil(t, branch.DeletedBy)
|
assert.NotNil(t, branch.DeletedBy)
|
||||||
assert.Equal(t, "Ghost", branch.DeletedBy.Name)
|
assert.Equal(t, "Ghost", branch.DeletedBy.Name)
|
||||||
}
|
}
|
||||||
@ -63,22 +77,22 @@ func TestRemoveDeletedBranch(t *testing.T) {
|
|||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
|
||||||
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
|
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
|
||||||
|
|
||||||
err := git_model.RemoveDeletedBranchByID(db.DefaultContext, repo.ID, 1)
|
err := git_model.RemoveDeletedBranchByID(db.DefaultContext, repo.ID, 1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
unittest.AssertNotExistsBean(t, firstBranch)
|
unittest.AssertNotExistsBean(t, firstBranch)
|
||||||
unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
|
unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 2})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDeletedBranch(t *testing.T, branch *git_model.DeletedBranch) *git_model.DeletedBranch {
|
func getDeletedBranch(t *testing.T, branch *git_model.Branch) *git_model.Branch {
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
|
||||||
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo.ID, branch.ID)
|
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo.ID, branch.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, branch.ID, deletedBranch.ID)
|
assert.Equal(t, branch.ID, deletedBranch.ID)
|
||||||
assert.Equal(t, branch.Name, deletedBranch.Name)
|
assert.Equal(t, branch.Name, deletedBranch.Name)
|
||||||
assert.Equal(t, branch.Commit, deletedBranch.Commit)
|
assert.Equal(t, branch.CommitID, deletedBranch.CommitID)
|
||||||
assert.Equal(t, branch.DeletedByID, deletedBranch.DeletedByID)
|
assert.Equal(t, branch.DeletedByID, deletedBranch.DeletedByID)
|
||||||
|
|
||||||
return deletedBranch
|
return deletedBranch
|
||||||
@ -146,8 +160,8 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) {
|
|||||||
|
|
||||||
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo2.ID, 1)
|
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo2.ID, 1)
|
||||||
|
|
||||||
// Expect no error, and the returned branch is nil.
|
// Expect error, and the returned branch is nil.
|
||||||
assert.NoError(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, deletedBranch)
|
assert.Nil(t, deletedBranch)
|
||||||
|
|
||||||
// Now get the deletedBranch with ID of 1 on repo with ID 1.
|
// Now get the deletedBranch with ID of 1 on repo with ID 1.
|
@ -1,197 +0,0 @@
|
|||||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DeletedBranch struct
|
|
||||||
type DeletedBranch struct {
|
|
||||||
ID int64 `xorm:"pk autoincr"`
|
|
||||||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
|
||||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
|
||||||
Commit string `xorm:"UNIQUE(s) NOT NULL"`
|
|
||||||
DeletedByID int64 `xorm:"INDEX"`
|
|
||||||
DeletedBy *user_model.User `xorm:"-"`
|
|
||||||
DeletedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
db.RegisterModel(new(DeletedBranch))
|
|
||||||
db.RegisterModel(new(RenamedBranch))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDeletedBranch adds a deleted branch to the database
|
|
||||||
func AddDeletedBranch(ctx context.Context, repoID int64, branchName, commit string, deletedByID int64) error {
|
|
||||||
deletedBranch := &DeletedBranch{
|
|
||||||
RepoID: repoID,
|
|
||||||
Name: branchName,
|
|
||||||
Commit: commit,
|
|
||||||
DeletedByID: deletedByID,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := db.GetEngine(ctx).Insert(deletedBranch)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDeletedBranches returns all the deleted branches
|
|
||||||
func GetDeletedBranches(ctx context.Context, repoID int64) ([]*DeletedBranch, error) {
|
|
||||||
deletedBranches := make([]*DeletedBranch, 0)
|
|
||||||
return deletedBranches, db.GetEngine(ctx).Where("repo_id = ?", repoID).Desc("deleted_unix").Find(&deletedBranches)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDeletedBranchByID get a deleted branch by its ID
|
|
||||||
func GetDeletedBranchByID(ctx context.Context, repoID, id int64) (*DeletedBranch, error) {
|
|
||||||
deletedBranch := &DeletedBranch{}
|
|
||||||
has, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).And("id = ?", id).Get(deletedBranch)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !has {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return deletedBranch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveDeletedBranchByID removes a deleted branch from the database
|
|
||||||
func RemoveDeletedBranchByID(ctx context.Context, repoID, id int64) (err error) {
|
|
||||||
deletedBranch := &DeletedBranch{
|
|
||||||
RepoID: repoID,
|
|
||||||
ID: id,
|
|
||||||
}
|
|
||||||
|
|
||||||
if affected, err := db.GetEngine(ctx).Delete(deletedBranch); err != nil {
|
|
||||||
return err
|
|
||||||
} else if affected != 1 {
|
|
||||||
return fmt.Errorf("remove deleted branch ID(%v) failed", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadUser loads the user that deleted the branch
|
|
||||||
// When there's no user found it returns a user_model.NewGhostUser
|
|
||||||
func (deletedBranch *DeletedBranch) LoadUser(ctx context.Context) {
|
|
||||||
user, err := user_model.GetUserByID(ctx, deletedBranch.DeletedByID)
|
|
||||||
if err != nil {
|
|
||||||
user = user_model.NewGhostUser()
|
|
||||||
}
|
|
||||||
deletedBranch.DeletedBy = user
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveDeletedBranchByName removes all deleted branches
|
|
||||||
func RemoveDeletedBranchByName(ctx context.Context, repoID int64, branch string) error {
|
|
||||||
_, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveOldDeletedBranches removes old deleted branches
|
|
||||||
func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
|
|
||||||
// Nothing to do for shutdown or terminate
|
|
||||||
log.Trace("Doing: DeletedBranchesCleanup")
|
|
||||||
|
|
||||||
deleteBefore := time.Now().Add(-olderThan)
|
|
||||||
_, err := db.GetEngine(ctx).Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("DeletedBranchesCleanup: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenamedBranch provide renamed branch log
|
|
||||||
// will check it when a branch can't be found
|
|
||||||
type RenamedBranch struct {
|
|
||||||
ID int64 `xorm:"pk autoincr"`
|
|
||||||
RepoID int64 `xorm:"INDEX NOT NULL"`
|
|
||||||
From string
|
|
||||||
To string
|
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindRenamedBranch check if a branch was renamed
|
|
||||||
func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
|
|
||||||
branch = &RenamedBranch{
|
|
||||||
RepoID: repoID,
|
|
||||||
From: from,
|
|
||||||
}
|
|
||||||
exist, err = db.GetEngine(ctx).Get(branch)
|
|
||||||
|
|
||||||
return branch, exist, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenameBranch rename a branch
|
|
||||||
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
// 1. update default branch if needed
|
|
||||||
isDefault := repo.DefaultBranch == from
|
|
||||||
if isDefault {
|
|
||||||
repo.DefaultBranch = to
|
|
||||||
_, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Update protected branch if needed
|
|
||||||
protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if protectedBranch != nil {
|
|
||||||
protectedBranch.RuleName = to
|
|
||||||
_, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
protected, err := IsBranchProtected(ctx, repo.ID, from)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if protected {
|
|
||||||
return ErrBranchIsProtected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Update all not merged pull request base branch name
|
|
||||||
_, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
|
|
||||||
repo.ID, from, false).
|
|
||||||
Update(map[string]interface{}{"base_branch": to})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. do git action
|
|
||||||
if err = gitAction(isDefault); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. insert renamed branch record
|
|
||||||
renamedBranch := &RenamedBranch{
|
|
||||||
RepoID: repo.ID,
|
|
||||||
From: from,
|
|
||||||
To: to,
|
|
||||||
}
|
|
||||||
err = db.Insert(ctx, renamedBranch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
)
|
)
|
||||||
@ -47,19 +47,32 @@ func FindRepoProtectedBranchRules(ctx context.Context, repoID int64) (ProtectedB
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindAllMatchedBranches find all matched branches
|
// FindAllMatchedBranches find all matched branches
|
||||||
func FindAllMatchedBranches(ctx context.Context, gitRepo *git.Repository, ruleName string) ([]string, error) {
|
func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) ([]string, error) {
|
||||||
// FIXME: how many should we get?
|
results := make([]string, 0, 10)
|
||||||
branches, _, err := gitRepo.GetBranchNames(0, 9999999)
|
for page := 1; ; page++ {
|
||||||
if err != nil {
|
brancheNames, err := FindBranchNames(ctx, FindBranchOptions{
|
||||||
return nil, err
|
ListOptions: db.ListOptions{
|
||||||
}
|
PageSize: 100,
|
||||||
rule := glob.MustCompile(ruleName)
|
Page: page,
|
||||||
results := make([]string, 0, len(branches))
|
},
|
||||||
for _, branch := range branches {
|
RepoID: repoID,
|
||||||
if rule.Match(branch) {
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
results = append(results, branch)
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rule := glob.MustCompile(ruleName)
|
||||||
|
|
||||||
|
for _, branch := range brancheNames {
|
||||||
|
if rule.Match(branch) {
|
||||||
|
results = append(results, branch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(brancheNames) < 100 {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +509,8 @@ var migrations = []Migration{
|
|||||||
NewMigration("Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
|
NewMigration("Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
|
||||||
// v263 -> v264
|
// v263 -> v264
|
||||||
NewMigration("Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
|
NewMigration("Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
|
||||||
|
// v264 -> v265
|
||||||
|
NewMigration("Add branch table", v1_21.AddBranchTable),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
93
models/migrations/v1_21/v264.go
Normal file
93
models/migrations/v1_21/v264.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_21 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddBranchTable(x *xorm.Engine) error {
|
||||||
|
type Branch struct {
|
||||||
|
ID int64
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||||
|
CommitID string
|
||||||
|
CommitMessage string `xorm:"TEXT"`
|
||||||
|
PusherID int64
|
||||||
|
IsDeleted bool `xorm:"index"`
|
||||||
|
DeletedByID int64
|
||||||
|
DeletedUnix timeutil.TimeStamp `xorm:"index"`
|
||||||
|
CommitTime timeutil.TimeStamp // The commit
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := x.Sync(new(Branch)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exist, err := x.IsTableExist("deleted_branches"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !exist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeletedBranch struct {
|
||||||
|
ID int64
|
||||||
|
RepoID int64 `xorm:"index UNIQUE(s)"`
|
||||||
|
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||||
|
Commit string
|
||||||
|
DeletedByID int64
|
||||||
|
DeletedUnix timeutil.TimeStamp
|
||||||
|
}
|
||||||
|
|
||||||
|
var adminUserID int64
|
||||||
|
has, err := x.Table("user").
|
||||||
|
Select("id").
|
||||||
|
Where("is_admin=?", true).
|
||||||
|
Asc("id"). // Reliably get the admin with the lowest ID.
|
||||||
|
Get(&adminUserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if !has {
|
||||||
|
return fmt.Errorf("no admin user found")
|
||||||
|
}
|
||||||
|
|
||||||
|
branches := make([]Branch, 0, 100)
|
||||||
|
if err := db.Iterate(context.Background(), nil, func(ctx context.Context, deletedBranch *DeletedBranch) error {
|
||||||
|
branches = append(branches, Branch{
|
||||||
|
RepoID: deletedBranch.RepoID,
|
||||||
|
Name: deletedBranch.Name,
|
||||||
|
CommitID: deletedBranch.Commit,
|
||||||
|
PusherID: adminUserID,
|
||||||
|
IsDeleted: true,
|
||||||
|
DeletedByID: deletedBranch.DeletedByID,
|
||||||
|
DeletedUnix: deletedBranch.DeletedUnix,
|
||||||
|
})
|
||||||
|
if len(branches) >= 100 {
|
||||||
|
_, err := x.Insert(&branches)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
branches = branches[:0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(branches) > 0 {
|
||||||
|
if _, err := x.Insert(&branches); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.DropTables("deleted_branches")
|
||||||
|
}
|
@ -147,7 +147,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||||||
&repo_model.Collaboration{RepoID: repoID},
|
&repo_model.Collaboration{RepoID: repoID},
|
||||||
&issues_model.Comment{RefRepoID: repoID},
|
&issues_model.Comment{RefRepoID: repoID},
|
||||||
&git_model.CommitStatus{RepoID: repoID},
|
&git_model.CommitStatus{RepoID: repoID},
|
||||||
&git_model.DeletedBranch{RepoID: repoID},
|
&git_model.Branch{RepoID: repoID},
|
||||||
&git_model.LFSLock{RepoID: repoID},
|
&git_model.LFSLock{RepoID: repoID},
|
||||||
&repo_model.LanguageStat{RepoID: repoID},
|
&repo_model.LanguageStat{RepoID: repoID},
|
||||||
&issues_model.Milestone{RepoID: repoID},
|
&issues_model.Milestone{RepoID: repoID},
|
||||||
|
@ -1171,9 +1171,9 @@ func GetUserByOpenID(uri string) (*User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAdminUser returns the first administrator
|
// GetAdminUser returns the first administrator
|
||||||
func GetAdminUser() (*User, error) {
|
func GetAdminUser(ctx context.Context) (*User, error) {
|
||||||
var admin User
|
var admin User
|
||||||
has, err := db.GetEngine(db.DefaultContext).
|
has, err := db.GetEngine(ctx).
|
||||||
Where("is_admin=?", true).
|
Where("is_admin=?", true).
|
||||||
Asc("id"). // Reliably get the admin with the lowest ID.
|
Asc("id"). // Reliably get the admin with the lowest ID.
|
||||||
Get(&admin)
|
Get(&admin)
|
||||||
|
@ -667,13 +667,38 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
|||||||
}
|
}
|
||||||
ctx.Data["Tags"] = tags
|
ctx.Data["Tags"] = tags
|
||||||
|
|
||||||
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
|
branchOpts := git_model.FindBranchOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
branchesTotal, err := git_model.CountBranches(ctx, branchOpts)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("CountBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// non empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
|
||||||
|
if branchesTotal == 0 { // fallback to do a sync immediately
|
||||||
|
branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("SyncRepoBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: use paganation and async loading
|
||||||
|
branchOpts.ExcludeBranchNames = []string{ctx.Repo.Repository.DefaultBranch}
|
||||||
|
brs, err := git_model.FindBranchNames(ctx, branchOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranches", err)
|
ctx.ServerError("GetBranches", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["Branches"] = brs
|
// always put default branch on the top
|
||||||
ctx.Data["BranchesCount"] = len(brs)
|
ctx.Data["Branches"] = append(branchOpts.ExcludeBranchNames, brs...)
|
||||||
|
ctx.Data["BranchesCount"] = branchesTotal
|
||||||
|
|
||||||
// If not branch selected, try default one.
|
// If not branch selected, try default one.
|
||||||
// If default branch doesn't exist, fall back to some other branch.
|
// If default branch doesn't exist, fall back to some other branch.
|
||||||
@ -897,9 +922,9 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
|||||||
if len(ctx.Params("*")) == 0 {
|
if len(ctx.Params("*")) == 0 {
|
||||||
refName = ctx.Repo.Repository.DefaultBranch
|
refName = ctx.Repo.Repository.DefaultBranch
|
||||||
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||||
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
|
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
|
||||||
if err == nil && len(brs) != 0 {
|
if err == nil && len(brs) != 0 {
|
||||||
refName = brs[0]
|
refName = 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)
|
||||||
ctx.Repo.Repository.MarkAsBrokenEmpty()
|
ctx.Repo.Repository.MarkAsBrokenEmpty()
|
||||||
|
@ -230,6 +230,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f382", "birthday cake", []string{"birthday"}, "6.0", false},
|
{"\U0001f382", "birthday cake", []string{"birthday"}, "6.0", false},
|
||||||
{"\U0001f9ac", "bison", []string{"bison"}, "13.0", false},
|
{"\U0001f9ac", "bison", []string{"bison"}, "13.0", false},
|
||||||
{"\U0001fae6", "biting lip", []string{"biting_lip"}, "14.0", false},
|
{"\U0001fae6", "biting lip", []string{"biting_lip"}, "14.0", false},
|
||||||
|
{"\U0001f426\u200d\u2b1b", "black bird", []string{"black_bird"}, "15.0", false},
|
||||||
{"\U0001f408\u200d\u2b1b", "black cat", []string{"black_cat"}, "13.0", false},
|
{"\U0001f408\u200d\u2b1b", "black cat", []string{"black_cat"}, "13.0", false},
|
||||||
{"\u26ab", "black circle", []string{"black_circle"}, "4.1", false},
|
{"\u26ab", "black circle", []string{"black_circle"}, "4.1", false},
|
||||||
{"\U0001f3f4", "black flag", []string{"black_flag"}, "7.0", false},
|
{"\U0001f3f4", "black flag", []string{"black_flag"}, "7.0", false},
|
||||||
@ -748,6 +749,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f42c", "dolphin", []string{"dolphin", "flipper"}, "6.0", false},
|
{"\U0001f42c", "dolphin", []string{"dolphin", "flipper"}, "6.0", false},
|
||||||
{"\U0001f1e9\U0001f1f2", "flag: Dominica", []string{"dominica"}, "6.0", false},
|
{"\U0001f1e9\U0001f1f2", "flag: Dominica", []string{"dominica"}, "6.0", false},
|
||||||
{"\U0001f1e9\U0001f1f4", "flag: Dominican Republic", []string{"dominican_republic"}, "6.0", false},
|
{"\U0001f1e9\U0001f1f4", "flag: Dominican Republic", []string{"dominican_republic"}, "6.0", false},
|
||||||
|
{"\U0001facf", "donkey", []string{"donkey"}, "15.0", false},
|
||||||
{"\U0001f6aa", "door", []string{"door"}, "6.0", false},
|
{"\U0001f6aa", "door", []string{"door"}, "6.0", false},
|
||||||
{"\U0001fae5", "dotted line face", []string{"dotted_line_face"}, "14.0", false},
|
{"\U0001fae5", "dotted line face", []string{"dotted_line_face"}, "14.0", false},
|
||||||
{"\U0001f369", "doughnut", []string{"doughnut"}, "6.0", false},
|
{"\U0001f369", "doughnut", []string{"doughnut"}, "6.0", false},
|
||||||
@ -982,11 +984,13 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f4be", "floppy disk", []string{"floppy_disk"}, "6.0", false},
|
{"\U0001f4be", "floppy disk", []string{"floppy_disk"}, "6.0", false},
|
||||||
{"\U0001f3b4", "flower playing cards", []string{"flower_playing_cards"}, "6.0", false},
|
{"\U0001f3b4", "flower playing cards", []string{"flower_playing_cards"}, "6.0", false},
|
||||||
{"\U0001f633", "flushed face", []string{"flushed"}, "6.0", false},
|
{"\U0001f633", "flushed face", []string{"flushed"}, "6.0", false},
|
||||||
|
{"\U0001fa88", "flute", []string{"flute"}, "15.0", false},
|
||||||
{"\U0001fab0", "fly", []string{"fly"}, "13.0", false},
|
{"\U0001fab0", "fly", []string{"fly"}, "13.0", false},
|
||||||
{"\U0001f94f", "flying disc", []string{"flying_disc"}, "11.0", false},
|
{"\U0001f94f", "flying disc", []string{"flying_disc"}, "11.0", false},
|
||||||
{"\U0001f6f8", "flying saucer", []string{"flying_saucer"}, "11.0", false},
|
{"\U0001f6f8", "flying saucer", []string{"flying_saucer"}, "11.0", false},
|
||||||
{"\U0001f32b\ufe0f", "fog", []string{"fog"}, "7.0", false},
|
{"\U0001f32b\ufe0f", "fog", []string{"fog"}, "7.0", false},
|
||||||
{"\U0001f301", "foggy", []string{"foggy"}, "6.0", false},
|
{"\U0001f301", "foggy", []string{"foggy"}, "6.0", false},
|
||||||
|
{"\U0001faad", "folding hand fan", []string{"folding_hand_fan"}, "15.0", false},
|
||||||
{"\U0001fad5", "fondue", []string{"fondue"}, "13.0", false},
|
{"\U0001fad5", "fondue", []string{"fondue"}, "13.0", false},
|
||||||
{"\U0001f9b6", "foot", []string{"foot"}, "11.0", true},
|
{"\U0001f9b6", "foot", []string{"foot"}, "11.0", true},
|
||||||
{"\U0001f9b6\U0001f3ff", "foot: Dark Skin Tone", []string{"foot_Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001f9b6\U0001f3ff", "foot: Dark Skin Tone", []string{"foot_Dark_Skin_Tone"}, "12.0", false},
|
||||||
@ -1054,6 +1058,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f1ec\U0001f1ee", "flag: Gibraltar", []string{"gibraltar"}, "6.0", false},
|
{"\U0001f1ec\U0001f1ee", "flag: Gibraltar", []string{"gibraltar"}, "6.0", false},
|
||||||
{"\U0001f381", "wrapped gift", []string{"gift"}, "6.0", false},
|
{"\U0001f381", "wrapped gift", []string{"gift"}, "6.0", false},
|
||||||
{"\U0001f49d", "heart with ribbon", []string{"gift_heart"}, "6.0", false},
|
{"\U0001f49d", "heart with ribbon", []string{"gift_heart"}, "6.0", false},
|
||||||
|
{"\U0001fada", "ginger root", []string{"ginger_root"}, "15.0", false},
|
||||||
{"\U0001f992", "giraffe", []string{"giraffe"}, "11.0", false},
|
{"\U0001f992", "giraffe", []string{"giraffe"}, "11.0", false},
|
||||||
{"\U0001f467", "girl", []string{"girl"}, "6.0", true},
|
{"\U0001f467", "girl", []string{"girl"}, "6.0", true},
|
||||||
{"\U0001f467\U0001f3ff", "girl: Dark Skin Tone", []string{"girl_Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001f467\U0001f3ff", "girl: Dark Skin Tone", []string{"girl_Dark_Skin_Tone"}, "12.0", false},
|
||||||
@ -1085,6 +1090,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f3cc\U0001f3fe\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Dark Skin Tone", []string{"golfing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001f3cc\U0001f3fe\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Dark Skin Tone", []string{"golfing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f3cc\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Light Skin Tone", []string{"golfing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
|
{"\U0001f3cc\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Light Skin Tone", []string{"golfing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f3cc\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium Skin Tone", []string{"golfing_woman_Medium_Skin_Tone"}, "12.0", false},
|
{"\U0001f3cc\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium Skin Tone", []string{"golfing_woman_Medium_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001fabf", "goose", []string{"goose"}, "15.0", false},
|
||||||
{"\U0001f98d", "gorilla", []string{"gorilla"}, "9.0", false},
|
{"\U0001f98d", "gorilla", []string{"gorilla"}, "9.0", false},
|
||||||
{"\U0001f347", "grapes", []string{"grapes"}, "6.0", false},
|
{"\U0001f347", "grapes", []string{"grapes"}, "6.0", false},
|
||||||
{"\U0001f1ec\U0001f1f7", "flag: Greece", []string{"greece"}, "6.0", false},
|
{"\U0001f1ec\U0001f1f7", "flag: Greece", []string{"greece"}, "6.0", false},
|
||||||
@ -1097,6 +1103,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f1ec\U0001f1f1", "flag: Greenland", []string{"greenland"}, "6.0", false},
|
{"\U0001f1ec\U0001f1f1", "flag: Greenland", []string{"greenland"}, "6.0", false},
|
||||||
{"\U0001f1ec\U0001f1e9", "flag: Grenada", []string{"grenada"}, "6.0", false},
|
{"\U0001f1ec\U0001f1e9", "flag: Grenada", []string{"grenada"}, "6.0", false},
|
||||||
{"\u2755", "white exclamation mark", []string{"grey_exclamation"}, "6.0", false},
|
{"\u2755", "white exclamation mark", []string{"grey_exclamation"}, "6.0", false},
|
||||||
|
{"\U0001fa76", "grey heart", []string{"grey_heart"}, "15.0", false},
|
||||||
{"\u2754", "white question mark", []string{"grey_question"}, "6.0", false},
|
{"\u2754", "white question mark", []string{"grey_question"}, "6.0", false},
|
||||||
{"\U0001f62c", "grimacing face", []string{"grimacing"}, "6.1", false},
|
{"\U0001f62c", "grimacing face", []string{"grimacing"}, "6.1", false},
|
||||||
{"\U0001f601", "beaming face with smiling eyes", []string{"grin"}, "6.0", false},
|
{"\U0001f601", "beaming face with smiling eyes", []string{"grin"}, "6.0", false},
|
||||||
@ -1129,6 +1136,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f3b8", "guitar", []string{"guitar"}, "6.0", false},
|
{"\U0001f3b8", "guitar", []string{"guitar"}, "6.0", false},
|
||||||
{"\U0001f52b", "water pistol", []string{"gun"}, "6.0", false},
|
{"\U0001f52b", "water pistol", []string{"gun"}, "6.0", false},
|
||||||
{"\U0001f1ec\U0001f1fe", "flag: Guyana", []string{"guyana"}, "6.0", false},
|
{"\U0001f1ec\U0001f1fe", "flag: Guyana", []string{"guyana"}, "6.0", false},
|
||||||
|
{"\U0001faae", "hair pick", []string{"hair_pick"}, "15.0", false},
|
||||||
{"\U0001f487", "person getting haircut", []string{"haircut"}, "6.0", true},
|
{"\U0001f487", "person getting haircut", []string{"haircut"}, "6.0", true},
|
||||||
{"\U0001f487\U0001f3ff", "person getting haircut: Dark Skin Tone", []string{"haircut_Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001f487\U0001f3ff", "person getting haircut: Dark Skin Tone", []string{"haircut_Dark_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f487\U0001f3fb", "person getting haircut: Light Skin Tone", []string{"haircut_Light_Skin_Tone"}, "12.0", false},
|
{"\U0001f487\U0001f3fb", "person getting haircut: Light Skin Tone", []string{"haircut_Light_Skin_Tone"}, "12.0", false},
|
||||||
@ -1253,6 +1261,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f1ed\U0001f1fa", "flag: Hungary", []string{"hungary"}, "6.0", false},
|
{"\U0001f1ed\U0001f1fa", "flag: Hungary", []string{"hungary"}, "6.0", false},
|
||||||
{"\U0001f62f", "hushed face", []string{"hushed"}, "6.1", false},
|
{"\U0001f62f", "hushed face", []string{"hushed"}, "6.1", false},
|
||||||
{"\U0001f6d6", "hut", []string{"hut"}, "13.0", false},
|
{"\U0001f6d6", "hut", []string{"hut"}, "13.0", false},
|
||||||
|
{"\U0001fabb", "hyacinth", []string{"hyacinth"}, "15.0", false},
|
||||||
{"\U0001f368", "ice cream", []string{"ice_cream"}, "6.0", false},
|
{"\U0001f368", "ice cream", []string{"ice_cream"}, "6.0", false},
|
||||||
{"\U0001f9ca", "ice", []string{"ice_cube"}, "12.0", false},
|
{"\U0001f9ca", "ice", []string{"ice_cube"}, "12.0", false},
|
||||||
{"\U0001f3d2", "ice hockey", []string{"ice_hockey"}, "8.0", false},
|
{"\U0001f3d2", "ice hockey", []string{"ice_hockey"}, "8.0", false},
|
||||||
@ -1293,6 +1302,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f479", "ogre", []string{"japanese_ogre"}, "6.0", false},
|
{"\U0001f479", "ogre", []string{"japanese_ogre"}, "6.0", false},
|
||||||
{"\U0001fad9", "jar", []string{"jar"}, "14.0", false},
|
{"\U0001fad9", "jar", []string{"jar"}, "14.0", false},
|
||||||
{"\U0001f456", "jeans", []string{"jeans"}, "6.0", false},
|
{"\U0001f456", "jeans", []string{"jeans"}, "6.0", false},
|
||||||
|
{"\U0001fabc", "jellyfish", []string{"jellyfish"}, "15.0", false},
|
||||||
{"\U0001f1ef\U0001f1ea", "flag: Jersey", []string{"jersey"}, "6.0", false},
|
{"\U0001f1ef\U0001f1ea", "flag: Jersey", []string{"jersey"}, "6.0", false},
|
||||||
{"\U0001f9e9", "puzzle piece", []string{"jigsaw"}, "11.0", false},
|
{"\U0001f9e9", "puzzle piece", []string{"jigsaw"}, "11.0", false},
|
||||||
{"\U0001f1ef\U0001f1f4", "flag: Jordan", []string{"jordan"}, "6.0", false},
|
{"\U0001f1ef\U0001f1f4", "flag: Jordan", []string{"jordan"}, "6.0", false},
|
||||||
@ -1319,6 +1329,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f511", "key", []string{"key"}, "6.0", false},
|
{"\U0001f511", "key", []string{"key"}, "6.0", false},
|
||||||
{"\u2328\ufe0f", "keyboard", []string{"keyboard"}, "", false},
|
{"\u2328\ufe0f", "keyboard", []string{"keyboard"}, "", false},
|
||||||
{"\U0001f51f", "keycap: 10", []string{"keycap_ten"}, "6.0", false},
|
{"\U0001f51f", "keycap: 10", []string{"keycap_ten"}, "6.0", false},
|
||||||
|
{"\U0001faaf", "khanda", []string{"khanda"}, "15.0", false},
|
||||||
{"\U0001f6f4", "kick scooter", []string{"kick_scooter"}, "9.0", false},
|
{"\U0001f6f4", "kick scooter", []string{"kick_scooter"}, "9.0", false},
|
||||||
{"\U0001f458", "kimono", []string{"kimono"}, "6.0", false},
|
{"\U0001f458", "kimono", []string{"kimono"}, "6.0", false},
|
||||||
{"\U0001f1f0\U0001f1ee", "flag: Kiribati", []string{"kiribati"}, "6.0", false},
|
{"\U0001f1f0\U0001f1ee", "flag: Kiribati", []string{"kiribati"}, "6.0", false},
|
||||||
@ -1383,6 +1394,12 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001faf2\U0001f3fe", "leftwards hand: Medium-Dark Skin Tone", []string{"leftwards_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001faf2\U0001f3fe", "leftwards hand: Medium-Dark Skin Tone", []string{"leftwards_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001faf2\U0001f3fc", "leftwards hand: Medium-Light Skin Tone", []string{"leftwards_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
{"\U0001faf2\U0001f3fc", "leftwards hand: Medium-Light Skin Tone", []string{"leftwards_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001faf2\U0001f3fd", "leftwards hand: Medium Skin Tone", []string{"leftwards_hand_Medium_Skin_Tone"}, "12.0", false},
|
{"\U0001faf2\U0001f3fd", "leftwards hand: Medium Skin Tone", []string{"leftwards_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf7", "leftwards pushing hand", []string{"leftwards_pushing_hand"}, "15.0", true},
|
||||||
|
{"\U0001faf7\U0001f3ff", "leftwards pushing hand: Dark Skin Tone", []string{"leftwards_pushing_hand_Dark_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf7\U0001f3fb", "leftwards pushing hand: Light Skin Tone", []string{"leftwards_pushing_hand_Light_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf7\U0001f3fe", "leftwards pushing hand: Medium-Dark Skin Tone", []string{"leftwards_pushing_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf7\U0001f3fc", "leftwards pushing hand: Medium-Light Skin Tone", []string{"leftwards_pushing_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf7\U0001f3fd", "leftwards pushing hand: Medium Skin Tone", []string{"leftwards_pushing_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f9b5", "leg", []string{"leg"}, "11.0", true},
|
{"\U0001f9b5", "leg", []string{"leg"}, "11.0", true},
|
||||||
{"\U0001f9b5\U0001f3ff", "leg: Dark Skin Tone", []string{"leg_Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001f9b5\U0001f3ff", "leg: Dark Skin Tone", []string{"leg_Dark_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f9b5\U0001f3fb", "leg: Light Skin Tone", []string{"leg_Light_Skin_Tone"}, "12.0", false},
|
{"\U0001f9b5\U0001f3fb", "leg: Light Skin Tone", []string{"leg_Light_Skin_Tone"}, "12.0", false},
|
||||||
@ -1398,6 +1415,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\u264e", "Libra", []string{"libra"}, "", false},
|
{"\u264e", "Libra", []string{"libra"}, "", false},
|
||||||
{"\U0001f1f1\U0001f1fe", "flag: Libya", []string{"libya"}, "6.0", false},
|
{"\U0001f1f1\U0001f1fe", "flag: Libya", []string{"libya"}, "6.0", false},
|
||||||
{"\U0001f1f1\U0001f1ee", "flag: Liechtenstein", []string{"liechtenstein"}, "6.0", false},
|
{"\U0001f1f1\U0001f1ee", "flag: Liechtenstein", []string{"liechtenstein"}, "6.0", false},
|
||||||
|
{"\U0001fa75", "light blue heart", []string{"light_blue_heart"}, "15.0", false},
|
||||||
{"\U0001f688", "light rail", []string{"light_rail"}, "6.0", false},
|
{"\U0001f688", "light rail", []string{"light_rail"}, "6.0", false},
|
||||||
{"\U0001f517", "link", []string{"link"}, "6.0", false},
|
{"\U0001f517", "link", []string{"link"}, "6.0", false},
|
||||||
{"\U0001f981", "lion", []string{"lion"}, "8.0", false},
|
{"\U0001f981", "lion", []string{"lion"}, "8.0", false},
|
||||||
@ -1695,6 +1713,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f570\ufe0f", "mantelpiece clock", []string{"mantelpiece_clock"}, "7.0", false},
|
{"\U0001f570\ufe0f", "mantelpiece clock", []string{"mantelpiece_clock"}, "7.0", false},
|
||||||
{"\U0001f9bd", "manual wheelchair", []string{"manual_wheelchair"}, "12.0", false},
|
{"\U0001f9bd", "manual wheelchair", []string{"manual_wheelchair"}, "12.0", false},
|
||||||
{"\U0001f341", "maple leaf", []string{"maple_leaf"}, "6.0", false},
|
{"\U0001f341", "maple leaf", []string{"maple_leaf"}, "6.0", false},
|
||||||
|
{"\U0001fa87", "maracas", []string{"maracas"}, "15.0", false},
|
||||||
{"\U0001f1f2\U0001f1ed", "flag: Marshall Islands", []string{"marshall_islands"}, "6.0", false},
|
{"\U0001f1f2\U0001f1ed", "flag: Marshall Islands", []string{"marshall_islands"}, "6.0", false},
|
||||||
{"\U0001f94b", "martial arts uniform", []string{"martial_arts_uniform"}, "9.0", false},
|
{"\U0001f94b", "martial arts uniform", []string{"martial_arts_uniform"}, "9.0", false},
|
||||||
{"\U0001f1f2\U0001f1f6", "flag: Martinique", []string{"martinique"}, "6.0", false},
|
{"\U0001f1f2\U0001f1f6", "flag: Martinique", []string{"martinique"}, "6.0", false},
|
||||||
@ -1799,6 +1818,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f1f2\U0001f1f8", "flag: Montserrat", []string{"montserrat"}, "6.0", false},
|
{"\U0001f1f2\U0001f1f8", "flag: Montserrat", []string{"montserrat"}, "6.0", false},
|
||||||
{"\U0001f314", "waxing gibbous moon", []string{"moon", "waxing_gibbous_moon"}, "6.0", false},
|
{"\U0001f314", "waxing gibbous moon", []string{"moon", "waxing_gibbous_moon"}, "6.0", false},
|
||||||
{"\U0001f96e", "moon cake", []string{"moon_cake"}, "11.0", false},
|
{"\U0001f96e", "moon cake", []string{"moon_cake"}, "11.0", false},
|
||||||
|
{"\U0001face", "moose", []string{"moose"}, "15.0", false},
|
||||||
{"\U0001f1f2\U0001f1e6", "flag: Morocco", []string{"morocco"}, "6.0", false},
|
{"\U0001f1f2\U0001f1e6", "flag: Morocco", []string{"morocco"}, "6.0", false},
|
||||||
{"\U0001f393", "graduation cap", []string{"mortar_board"}, "6.0", false},
|
{"\U0001f393", "graduation cap", []string{"mortar_board"}, "6.0", false},
|
||||||
{"\U0001f54c", "mosque", []string{"mosque"}, "8.0", false},
|
{"\U0001f54c", "mosque", []string{"mosque"}, "8.0", false},
|
||||||
@ -2076,6 +2096,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f6f3\ufe0f", "passenger ship", []string{"passenger_ship"}, "7.0", false},
|
{"\U0001f6f3\ufe0f", "passenger ship", []string{"passenger_ship"}, "7.0", false},
|
||||||
{"\U0001f6c2", "passport control", []string{"passport_control"}, "6.0", false},
|
{"\U0001f6c2", "passport control", []string{"passport_control"}, "6.0", false},
|
||||||
{"\u23f8\ufe0f", "pause button", []string{"pause_button"}, "7.0", false},
|
{"\u23f8\ufe0f", "pause button", []string{"pause_button"}, "7.0", false},
|
||||||
|
{"\U0001fadb", "pea pod", []string{"pea_pod"}, "15.0", false},
|
||||||
{"\u262e\ufe0f", "peace symbol", []string{"peace_symbol"}, "", false},
|
{"\u262e\ufe0f", "peace symbol", []string{"peace_symbol"}, "", false},
|
||||||
{"\U0001f351", "peach", []string{"peach"}, "6.0", false},
|
{"\U0001f351", "peach", []string{"peach"}, "6.0", false},
|
||||||
{"\U0001f99a", "peacock", []string{"peacock"}, "11.0", false},
|
{"\U0001f99a", "peacock", []string{"peacock"}, "11.0", false},
|
||||||
@ -2085,7 +2106,12 @@ var GemojiData = Gemoji{
|
|||||||
{"\u270f\ufe0f", "pencil", []string{"pencil2"}, "", false},
|
{"\u270f\ufe0f", "pencil", []string{"pencil2"}, "", false},
|
||||||
{"\U0001f427", "penguin", []string{"penguin"}, "6.0", false},
|
{"\U0001f427", "penguin", []string{"penguin"}, "6.0", false},
|
||||||
{"\U0001f614", "pensive face", []string{"pensive"}, "6.0", false},
|
{"\U0001f614", "pensive face", []string{"pensive"}, "6.0", false},
|
||||||
{"\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands", []string{"people_holding_hands"}, "12.0", false},
|
{"\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands", []string{"people_holding_hands"}, "12.0", true},
|
||||||
|
{"\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Dark Skin Tone", []string{"people_holding_hands_Dark_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Light Skin Tone", []string{"people_holding_hands_Light_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Dark Skin Tone", []string{"people_holding_hands_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Light Skin Tone", []string{"people_holding_hands_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium Skin Tone", []string{"people_holding_hands_Medium_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001fac2", "people hugging", []string{"people_hugging"}, "13.0", false},
|
{"\U0001fac2", "people hugging", []string{"people_hugging"}, "13.0", false},
|
||||||
{"\U0001f3ad", "performing arts", []string{"performing_arts"}, "6.0", false},
|
{"\U0001f3ad", "performing arts", []string{"performing_arts"}, "6.0", false},
|
||||||
{"\U0001f623", "persevering face", []string{"persevere"}, "6.0", false},
|
{"\U0001f623", "persevering face", []string{"persevere"}, "6.0", false},
|
||||||
@ -2194,6 +2220,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f90f\U0001f3fd", "pinching hand: Medium Skin Tone", []string{"pinching_hand_Medium_Skin_Tone"}, "12.0", false},
|
{"\U0001f90f\U0001f3fd", "pinching hand: Medium Skin Tone", []string{"pinching_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f34d", "pineapple", []string{"pineapple"}, "6.0", false},
|
{"\U0001f34d", "pineapple", []string{"pineapple"}, "6.0", false},
|
||||||
{"\U0001f3d3", "ping pong", []string{"ping_pong"}, "8.0", false},
|
{"\U0001f3d3", "ping pong", []string{"ping_pong"}, "8.0", false},
|
||||||
|
{"\U0001fa77", "pink heart", []string{"pink_heart"}, "15.0", false},
|
||||||
{"\U0001f3f4\u200d\u2620\ufe0f", "pirate flag", []string{"pirate_flag"}, "11.0", false},
|
{"\U0001f3f4\u200d\u2620\ufe0f", "pirate flag", []string{"pirate_flag"}, "11.0", false},
|
||||||
{"\u2653", "Pisces", []string{"pisces"}, "", false},
|
{"\u2653", "Pisces", []string{"pisces"}, "", false},
|
||||||
{"\U0001f1f5\U0001f1f3", "flag: Pitcairn Islands", []string{"pitcairn_islands"}, "6.0", false},
|
{"\U0001f1f5\U0001f1f3", "flag: Pitcairn Islands", []string{"pitcairn_islands"}, "6.0", false},
|
||||||
@ -2346,7 +2373,7 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f4fb", "radio", []string{"radio"}, "6.0", false},
|
{"\U0001f4fb", "radio", []string{"radio"}, "6.0", false},
|
||||||
{"\U0001f518", "radio button", []string{"radio_button"}, "6.0", false},
|
{"\U0001f518", "radio button", []string{"radio_button"}, "6.0", false},
|
||||||
{"\u2622\ufe0f", "radioactive", []string{"radioactive"}, "", false},
|
{"\u2622\ufe0f", "radioactive", []string{"radioactive"}, "", false},
|
||||||
{"\U0001f621", "pouting face", []string{"rage", "pout"}, "6.0", false},
|
{"\U0001f621", "enraged face", []string{"rage", "pout"}, "6.0", false},
|
||||||
{"\U0001f683", "railway car", []string{"railway_car"}, "6.0", false},
|
{"\U0001f683", "railway car", []string{"railway_car"}, "6.0", false},
|
||||||
{"\U0001f6e4\ufe0f", "railway track", []string{"railway_track"}, "7.0", false},
|
{"\U0001f6e4\ufe0f", "railway track", []string{"railway_track"}, "7.0", false},
|
||||||
{"\U0001f308", "rainbow", []string{"rainbow"}, "6.0", false},
|
{"\U0001f308", "rainbow", []string{"rainbow"}, "6.0", false},
|
||||||
@ -2434,6 +2461,12 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001faf1\U0001f3fe", "rightwards hand: Medium-Dark Skin Tone", []string{"rightwards_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001faf1\U0001f3fe", "rightwards hand: Medium-Dark Skin Tone", []string{"rightwards_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001faf1\U0001f3fc", "rightwards hand: Medium-Light Skin Tone", []string{"rightwards_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
{"\U0001faf1\U0001f3fc", "rightwards hand: Medium-Light Skin Tone", []string{"rightwards_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001faf1\U0001f3fd", "rightwards hand: Medium Skin Tone", []string{"rightwards_hand_Medium_Skin_Tone"}, "12.0", false},
|
{"\U0001faf1\U0001f3fd", "rightwards hand: Medium Skin Tone", []string{"rightwards_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf8", "rightwards pushing hand", []string{"rightwards_pushing_hand"}, "15.0", true},
|
||||||
|
{"\U0001faf8\U0001f3ff", "rightwards pushing hand: Dark Skin Tone", []string{"rightwards_pushing_hand_Dark_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf8\U0001f3fb", "rightwards pushing hand: Light Skin Tone", []string{"rightwards_pushing_hand_Light_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf8\U0001f3fe", "rightwards pushing hand: Medium-Dark Skin Tone", []string{"rightwards_pushing_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf8\U0001f3fc", "rightwards pushing hand: Medium-Light Skin Tone", []string{"rightwards_pushing_hand_Medium-Light_Skin_Tone"}, "12.0", false},
|
||||||
|
{"\U0001faf8\U0001f3fd", "rightwards pushing hand: Medium Skin Tone", []string{"rightwards_pushing_hand_Medium_Skin_Tone"}, "12.0", false},
|
||||||
{"\U0001f48d", "ring", []string{"ring"}, "6.0", false},
|
{"\U0001f48d", "ring", []string{"ring"}, "6.0", false},
|
||||||
{"\U0001f6df", "ring buoy", []string{"ring_buoy"}, "14.0", false},
|
{"\U0001f6df", "ring buoy", []string{"ring_buoy"}, "14.0", false},
|
||||||
{"\U0001fa90", "ringed planet", []string{"ringed_planet"}, "12.0", false},
|
{"\U0001fa90", "ringed planet", []string{"ringed_planet"}, "12.0", false},
|
||||||
@ -2566,6 +2599,7 @@ var GemojiData = Gemoji{
|
|||||||
{"7\ufe0f\u20e3", "keycap: 7", []string{"seven"}, "", false},
|
{"7\ufe0f\u20e3", "keycap: 7", []string{"seven"}, "", false},
|
||||||
{"\U0001faa1", "sewing needle", []string{"sewing_needle"}, "13.0", false},
|
{"\U0001faa1", "sewing needle", []string{"sewing_needle"}, "13.0", false},
|
||||||
{"\U0001f1f8\U0001f1e8", "flag: Seychelles", []string{"seychelles"}, "6.0", false},
|
{"\U0001f1f8\U0001f1e8", "flag: Seychelles", []string{"seychelles"}, "6.0", false},
|
||||||
|
{"\U0001fae8", "shaking face", []string{"shaking_face"}, "15.0", false},
|
||||||
{"\U0001f958", "shallow pan of food", []string{"shallow_pan_of_food"}, "", false},
|
{"\U0001f958", "shallow pan of food", []string{"shallow_pan_of_food"}, "", false},
|
||||||
{"\u2618\ufe0f", "shamrock", []string{"shamrock"}, "4.1", false},
|
{"\u2618\ufe0f", "shamrock", []string{"shamrock"}, "4.1", false},
|
||||||
{"\U0001f988", "shark", []string{"shark"}, "9.0", false},
|
{"\U0001f988", "shark", []string{"shark"}, "9.0", false},
|
||||||
@ -3125,7 +3159,9 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f32c\ufe0f", "wind face", []string{"wind_face"}, "7.0", false},
|
{"\U0001f32c\ufe0f", "wind face", []string{"wind_face"}, "7.0", false},
|
||||||
{"\U0001fa9f", "window", []string{"window"}, "13.0", false},
|
{"\U0001fa9f", "window", []string{"window"}, "13.0", false},
|
||||||
{"\U0001f377", "wine glass", []string{"wine_glass"}, "6.0", false},
|
{"\U0001f377", "wine glass", []string{"wine_glass"}, "6.0", false},
|
||||||
|
{"\U0001fabd", "wing", []string{"wing"}, "15.0", false},
|
||||||
{"\U0001f609", "winking face", []string{"wink"}, "6.0", false},
|
{"\U0001f609", "winking face", []string{"wink"}, "6.0", false},
|
||||||
|
{"\U0001f6dc", "wireless", []string{"wireless"}, "15.0", false},
|
||||||
{"\U0001f43a", "wolf", []string{"wolf"}, "6.0", false},
|
{"\U0001f43a", "wolf", []string{"wolf"}, "6.0", false},
|
||||||
{"\U0001f469", "woman", []string{"woman"}, "6.0", true},
|
{"\U0001f469", "woman", []string{"woman"}, "6.0", true},
|
||||||
{"\U0001f469\U0001f3ff", "woman: Dark Skin Tone", []string{"woman_Dark_Skin_Tone"}, "12.0", false},
|
{"\U0001f469\U0001f3ff", "woman: Dark Skin Tone", []string{"woman_Dark_Skin_Tone"}, "12.0", false},
|
||||||
@ -3364,5 +3400,5 @@ var GemojiData = Gemoji{
|
|||||||
{"\U0001f9df", "zombie", []string{"zombie"}, "11.0", false},
|
{"\U0001f9df", "zombie", []string{"zombie"}, "11.0", false},
|
||||||
{"\U0001f9df\u200d\u2642\ufe0f", "man zombie", []string{"zombie_man"}, "11.0", false},
|
{"\U0001f9df\u200d\u2642\ufe0f", "man zombie", []string{"zombie_man"}, "11.0", false},
|
||||||
{"\U0001f9df\u200d\u2640\ufe0f", "woman zombie", []string{"zombie_woman"}, "11.0", false},
|
{"\U0001f9df\u200d\u2640\ufe0f", "woman zombie", []string{"zombie_woman"}, "11.0", false},
|
||||||
{"\U0001f4a4", "zzz", []string{"zzz"}, "6.0", false},
|
{"\U0001f4a4", "ZZZ", []string{"zzz"}, "6.0", false},
|
||||||
}
|
}
|
||||||
|
135
modules/repository/branch.go
Normal file
135
modules/repository/branch.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SyncRepoBranches synchronizes branch table with repository branches
|
||||||
|
func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) {
|
||||||
|
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("SyncRepoBranches: in Repo[%d:%s]", repo.ID, repo.FullName())
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("OpenRepository[%s]: %w", repo.RepoPath(), err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
return SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, error) {
|
||||||
|
allBranches := container.Set[string]{}
|
||||||
|
{
|
||||||
|
branches, _, err := gitRepo.GetBranchNames(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches)
|
||||||
|
for _, branch := range branches {
|
||||||
|
allBranches.Add(branch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbBranches := make(map[string]*git_model.Branch)
|
||||||
|
{
|
||||||
|
branches, err := git_model.FindBranches(ctx, git_model.FindBranchOptions{
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
RepoID: repo.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for _, branch := range branches {
|
||||||
|
dbBranches[branch.Name] = branch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var toAdd []*git_model.Branch
|
||||||
|
var toUpdate []*git_model.Branch
|
||||||
|
var toRemove []int64
|
||||||
|
for branch := range allBranches {
|
||||||
|
dbb := dbBranches[branch]
|
||||||
|
commit, err := gitRepo.GetBranchCommit(branch)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if dbb == nil {
|
||||||
|
toAdd = append(toAdd, &git_model.Branch{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Name: branch,
|
||||||
|
CommitID: commit.ID.String(),
|
||||||
|
CommitMessage: commit.CommitMessage,
|
||||||
|
PusherID: doerID,
|
||||||
|
CommitTime: timeutil.TimeStamp(commit.Author.When.Unix()),
|
||||||
|
})
|
||||||
|
} else if commit.ID.String() != dbb.CommitID {
|
||||||
|
toUpdate = append(toUpdate, &git_model.Branch{
|
||||||
|
ID: dbb.ID,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Name: branch,
|
||||||
|
CommitID: commit.ID.String(),
|
||||||
|
CommitMessage: commit.CommitMessage,
|
||||||
|
PusherID: doerID,
|
||||||
|
CommitTime: timeutil.TimeStamp(commit.Author.When.Unix()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dbBranch := range dbBranches {
|
||||||
|
if !allBranches.Contains(dbBranch.Name) && !dbBranch.IsDeleted {
|
||||||
|
toRemove = append(toRemove, dbBranch.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("SyncRepoBranches[%s]: toAdd: %v, toUpdate: %v, toRemove: %v", repo.FullName(), toAdd, toUpdate, toRemove)
|
||||||
|
|
||||||
|
if len(toAdd) == 0 && len(toRemove) == 0 && len(toUpdate) == 0 {
|
||||||
|
return int64(len(allBranches)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.WithTx(ctx, func(subCtx context.Context) error {
|
||||||
|
if len(toAdd) > 0 {
|
||||||
|
if err := git_model.AddBranches(subCtx, toAdd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, b := range toUpdate {
|
||||||
|
if _, err := db.GetEngine(subCtx).ID(b.ID).
|
||||||
|
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted").
|
||||||
|
Update(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(toRemove) > 0 {
|
||||||
|
if err := git_model.DeleteBranches(subCtx, repo.ID, doerID, toRemove); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int64(len(allBranches)), nil
|
||||||
|
}
|
@ -351,6 +351,12 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re
|
|||||||
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
||||||
return fmt.Errorf("setDefaultBranch: %w", err)
|
return fmt.Errorf("setDefaultBranch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !repo.IsEmpty {
|
||||||
|
if _, err := SyncRepoBranches(ctx, repo.ID, u.ID); err != nil {
|
||||||
|
return fmt.Errorf("SyncRepoBranches: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = UpdateRepository(ctx, repo, false); err != nil {
|
if err = UpdateRepository(ctx, repo, false); err != nil {
|
||||||
|
@ -151,6 +151,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil {
|
||||||
|
return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if !opts.Releases {
|
if !opts.Releases {
|
||||||
// note: this will greatly improve release (tag) sync
|
// note: this will greatly improve release (tag) sync
|
||||||
// for pull-mirrors with many tags
|
// for pull-mirrors with many tags
|
||||||
@ -169,7 +173,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(db.DefaultContext)
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -380,3 +380,9 @@ type NewIssuePinsAllowed struct {
|
|||||||
Issues bool `json:"issues"`
|
Issues bool `json:"issues"`
|
||||||
PullRequests bool `json:"pull_requests"`
|
PullRequests bool `json:"pull_requests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRepoAvatarUserOption options when updating the repo avatar
|
||||||
|
type UpdateRepoAvatarOption struct {
|
||||||
|
// image must be base64 encoded
|
||||||
|
Image string `json:"image" binding:"Required"`
|
||||||
|
}
|
||||||
|
@ -102,3 +102,9 @@ type RenameUserOption struct {
|
|||||||
// unique: true
|
// unique: true
|
||||||
NewName string `json:"new_username" binding:"Required"`
|
NewName string `json:"new_username" binding:"Required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserAvatarUserOption options when updating the user avatar
|
||||||
|
type UpdateUserAvatarOption struct {
|
||||||
|
// image must be base64 encoded
|
||||||
|
Image string `json:"image" binding:"Required"`
|
||||||
|
}
|
||||||
|
@ -2660,6 +2660,7 @@ dashboard.delete_repo_archives.started = Delete all repository archives task sta
|
|||||||
dashboard.delete_missing_repos = Delete all repositories missing their Git files
|
dashboard.delete_missing_repos = Delete all repositories missing their Git files
|
||||||
dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started.
|
dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started.
|
||||||
dashboard.delete_generated_repository_avatars = Delete generated repository avatars
|
dashboard.delete_generated_repository_avatars = Delete generated repository avatars
|
||||||
|
dashboard.sync_repo_branches = Sync missed branches from git data to databases
|
||||||
dashboard.update_mirrors = Update Mirrors
|
dashboard.update_mirrors = Update Mirrors
|
||||||
dashboard.repo_health_check = Health check all repositories
|
dashboard.repo_health_check = Health check all repositories
|
||||||
dashboard.check_repo_stats = Check all repository statistics
|
dashboard.check_repo_stats = Check all repository statistics
|
||||||
@ -2713,6 +2714,7 @@ dashboard.gc_lfs = Garbage collect LFS meta objects
|
|||||||
dashboard.stop_zombie_tasks = Stop zombie tasks
|
dashboard.stop_zombie_tasks = Stop zombie tasks
|
||||||
dashboard.stop_endless_tasks = Stop endless tasks
|
dashboard.stop_endless_tasks = Stop endless tasks
|
||||||
dashboard.cancel_abandoned_jobs = Cancel abandoned jobs
|
dashboard.cancel_abandoned_jobs = Cancel abandoned jobs
|
||||||
|
dashboard.sync_branch.started = Branches Sync started
|
||||||
|
|
||||||
users.user_manage_panel = User Account Management
|
users.user_manage_panel = User Account Management
|
||||||
users.new_account = Create User Account
|
users.new_account = Create User Account
|
||||||
|
@ -899,6 +899,11 @@ func Routes() *web.Route {
|
|||||||
Patch(bind(api.EditHookOption{}), user.EditHook).
|
Patch(bind(api.EditHookOption{}), user.EditHook).
|
||||||
Delete(user.DeleteHook)
|
Delete(user.DeleteHook)
|
||||||
}, reqWebhooksEnabled())
|
}, reqWebhooksEnabled())
|
||||||
|
|
||||||
|
m.Group("/avatar", func() {
|
||||||
|
m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar)
|
||||||
|
m.Delete("", user.DeleteAvatar)
|
||||||
|
}, reqToken())
|
||||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
|
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
|
||||||
|
|
||||||
// Repositories (requires repo scope, org scope)
|
// Repositories (requires repo scope, org scope)
|
||||||
@ -1134,6 +1139,10 @@ func Routes() *web.Route {
|
|||||||
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
|
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
|
||||||
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
|
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
|
||||||
m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed)
|
m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed)
|
||||||
|
m.Group("/avatar", func() {
|
||||||
|
m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar)
|
||||||
|
m.Delete("", repo.DeleteAvatar)
|
||||||
|
}, reqAdmin(), reqToken())
|
||||||
}, repoAssignment())
|
}, repoAssignment())
|
||||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
|
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
|
||||||
|
|
||||||
@ -1314,6 +1323,10 @@ func Routes() *web.Route {
|
|||||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||||
Delete(org.DeleteHook)
|
Delete(org.DeleteHook)
|
||||||
}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled())
|
}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled())
|
||||||
|
m.Group("/avatar", func() {
|
||||||
|
m.Post("", bind(api.UpdateUserAvatarOption{}), org.UpdateAvatar)
|
||||||
|
m.Delete("", org.DeleteAvatar)
|
||||||
|
}, reqToken(), reqOrgOwnership())
|
||||||
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
|
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
|
||||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
|
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
|
||||||
m.Group("/teams/{teamid}", func() {
|
m.Group("/teams/{teamid}", func() {
|
||||||
|
74
routers/api/v1/org/avatar.go
Normal file
74
routers/api/v1/org/avatar.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package org
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateAvatarupdates the Avatar of an Organisation
|
||||||
|
func UpdateAvatar(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar
|
||||||
|
// ---
|
||||||
|
// summary: Update Avatar
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: org
|
||||||
|
// in: path
|
||||||
|
// description: name of the organization
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/UpdateUserAvatarOption"
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
|
||||||
|
|
||||||
|
content, err := base64.StdEncoding.DecodeString(form.Image)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user_service.UploadAvatar(ctx.Org.Organization.AsUser(), content)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAvatar deletes the Avatar of an Organisation
|
||||||
|
func DeleteAvatar(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar
|
||||||
|
// ---
|
||||||
|
// summary: Delete Avatar
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: org
|
||||||
|
// in: path
|
||||||
|
// description: name of the organization
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
err := user_service.DeleteAvatar(ctx.Org.Organization.AsUser())
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
84
routers/api/v1/repo/avatar.go
Normal file
84
routers/api/v1/repo/avatar.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateVatar updates the Avatar of an Repo
|
||||||
|
func UpdateAvatar(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar
|
||||||
|
// ---
|
||||||
|
// summary: Update avatar
|
||||||
|
// 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: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/UpdateRepoAvatarOption"
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
form := web.GetForm(ctx).(*api.UpdateRepoAvatarOption)
|
||||||
|
|
||||||
|
content, err := base64.StdEncoding.DecodeString(form.Image)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = repo_service.UploadAvatar(ctx, ctx.Repo.Repository, content)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAvatar deletes the Avatar of an Repo
|
||||||
|
func DeleteAvatar(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar
|
||||||
|
// ---
|
||||||
|
// summary: Delete avatar
|
||||||
|
// 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
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
@ -15,7 +15,9 @@ import (
|
|||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
@ -76,7 +78,7 @@ func GetBranch(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||||
return
|
return
|
||||||
@ -118,6 +120,37 @@ func DeleteBranch(ctx *context.APIContext) {
|
|||||||
|
|
||||||
branchName := ctx.Params("*")
|
branchName := ctx.Params("*")
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "Git Repository is empty.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether branches of this repository has been synced
|
||||||
|
totalNumOfBranches, err := git_model.CountBranches(ctx, git_model.FindBranchOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "CountBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
|
||||||
|
_, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("SyncRepoBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsArchived {
|
||||||
|
ctx.Error(http.StatusForbidden, "IsArchived", fmt.Errorf("can not delete branch of an archived repository"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.Repo.Repository.IsMirror {
|
||||||
|
ctx.Error(http.StatusForbidden, "IsMirrored", fmt.Errorf("can not delete branch of an mirror repository"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
|
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case git.IsErrBranchNotExist(err):
|
case git.IsErrBranchNotExist(err):
|
||||||
@ -203,14 +236,14 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
|
|
||||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
|
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrBranchDoesNotExist(err) {
|
if git_model.IsErrBranchNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||||
}
|
}
|
||||||
if models.IsErrTagAlreadyExists(err) {
|
if models.IsErrTagAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||||
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||||
} else if models.IsErrBranchNameConflict(err) {
|
} else if git_model.IsErrBranchNameConflict(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "CreateNewBranchFromCommit", err)
|
ctx.Error(http.StatusInternalServerError, "CreateNewBranchFromCommit", err)
|
||||||
@ -236,7 +269,7 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||||
return
|
return
|
||||||
@ -275,20 +308,38 @@ func ListBranches(ctx *context.APIContext) {
|
|||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/BranchList"
|
// "$ref": "#/responses/BranchList"
|
||||||
|
|
||||||
var totalNumOfBranches int
|
var totalNumOfBranches int64
|
||||||
var apiBranches []*api.Branch
|
var apiBranches []*api.Branch
|
||||||
|
|
||||||
listOptions := utils.GetListOptions(ctx)
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil {
|
if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil {
|
||||||
|
branchOpts := git_model.FindBranchOptions{
|
||||||
|
ListOptions: listOptions,
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
totalNumOfBranches, err = git_model.CountBranches(ctx, branchOpts)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "CountBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
|
||||||
|
totalNumOfBranches, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("SyncRepoBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
|
ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
skip, _ := listOptions.GetStartEnd()
|
branches, err := git_model.FindBranches(ctx, branchOpts)
|
||||||
branches, total, err := ctx.Repo.GitRepo.GetBranches(skip, listOptions.PageSize)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
||||||
return
|
return
|
||||||
@ -296,11 +347,11 @@ func ListBranches(ctx *context.APIContext) {
|
|||||||
|
|
||||||
apiBranches = make([]*api.Branch, 0, len(branches))
|
apiBranches = make([]*api.Branch, 0, len(branches))
|
||||||
for i := range branches {
|
for i := range branches {
|
||||||
c, err := branches[i].GetCommit()
|
c, err := ctx.Repo.GitRepo.GetBranchCommit(branches[i].Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Skip if this branch doesn't exist anymore.
|
// Skip if this branch doesn't exist anymore.
|
||||||
if git.IsErrNotExist(err) {
|
if git.IsErrNotExist(err) {
|
||||||
total--
|
totalNumOfBranches--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||||
@ -308,19 +359,17 @@ func ListBranches(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
branchProtection := rules.GetFirstMatched(branches[i].Name)
|
branchProtection := rules.GetFirstMatched(branches[i].Name)
|
||||||
apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i].Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
apiBranches = append(apiBranches, apiBranch)
|
apiBranches = append(apiBranches, apiBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
totalNumOfBranches = total
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
|
ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
|
||||||
ctx.SetTotalCountHeader(int64(totalNumOfBranches))
|
ctx.SetTotalCountHeader(totalNumOfBranches)
|
||||||
ctx.JSON(http.StatusOK, apiBranches)
|
ctx.JSON(http.StatusOK, apiBranches)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,7 +629,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, ruleName)
|
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, ruleName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
||||||
return
|
return
|
||||||
@ -851,7 +900,7 @@ func EditBranchProtection(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, protectBranch.RuleName)
|
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
||||||
return
|
return
|
||||||
|
@ -687,12 +687,12 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
|
|||||||
ctx.Error(http.StatusForbidden, "Access", err)
|
ctx.Error(http.StatusForbidden, "Access", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
||||||
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrBranchDoesNotExist(err) || git.IsErrBranchNotExist(err) {
|
if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
|
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -843,7 +843,7 @@ func DeleteFile(ctx *context.APIContext) {
|
|||||||
if git.IsErrBranchNotExist(err) || models.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
|
if git.IsErrBranchNotExist(err) || models.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "DeleteFile", err)
|
ctx.Error(http.StatusNotFound, "DeleteFile", err)
|
||||||
return
|
return
|
||||||
} else if models.IsErrBranchAlreadyExists(err) ||
|
} else if git_model.IsErrBranchAlreadyExists(err) ||
|
||||||
models.IsErrFilenameInvalid(err) ||
|
models.IsErrFilenameInvalid(err) ||
|
||||||
models.IsErrSHADoesNotMatch(err) ||
|
models.IsErrSHADoesNotMatch(err) ||
|
||||||
models.IsErrCommitIDDoesNotMatch(err) ||
|
models.IsErrCommitIDDoesNotMatch(err) ||
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
@ -91,12 +92,12 @@ func ApplyDiffPatch(ctx *context.APIContext) {
|
|||||||
ctx.Error(http.StatusForbidden, "Access", err)
|
ctx.Error(http.StatusForbidden, "Access", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
||||||
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrBranchDoesNotExist(err) || git.IsErrBranchNotExist(err) {
|
if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
|
ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -181,4 +181,10 @@ type swaggerParameterBodies struct {
|
|||||||
|
|
||||||
// in:body
|
// in:body
|
||||||
CreatePushMirrorOption api.CreatePushMirrorOption
|
CreatePushMirrorOption api.CreatePushMirrorOption
|
||||||
|
|
||||||
|
// in:body
|
||||||
|
UpdateUserAvatarOptions api.UpdateUserAvatarOption
|
||||||
|
|
||||||
|
// in:body
|
||||||
|
UpdateRepoAvatarOptions api.UpdateRepoAvatarOption
|
||||||
}
|
}
|
||||||
|
63
routers/api/v1/user/avatar.go
Normal file
63
routers/api/v1/user/avatar.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateAvatar updates the Avatar of an User
|
||||||
|
func UpdateAvatar(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /user/avatar user userUpdateAvatar
|
||||||
|
// ---
|
||||||
|
// summary: Update Avatar
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/UpdateUserAvatarOption"
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
|
||||||
|
|
||||||
|
content, err := base64.StdEncoding.DecodeString(form.Image)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user_service.UploadAvatar(ctx.Doer, content)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAvatar deletes the Avatar of an User
|
||||||
|
func DeleteAvatar(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /user/avatar user userDeleteAvatar
|
||||||
|
// ---
|
||||||
|
// summary: Delete Avatar
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
err := user_service.DeleteAvatar(ctx.Doer)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
@ -14,12 +14,15 @@ import (
|
|||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/updatechecker"
|
"code.gitea.io/gitea/modules/updatechecker"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/services/cron"
|
"code.gitea.io/gitea/services/cron"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -133,12 +136,22 @@ func DashboardPost(ctx *context.Context) {
|
|||||||
|
|
||||||
// Run operation.
|
// Run operation.
|
||||||
if form.Op != "" {
|
if form.Op != "" {
|
||||||
task := cron.GetTask(form.Op)
|
switch form.Op {
|
||||||
if task != nil {
|
case "sync_repo_branches":
|
||||||
go task.RunWithUser(ctx.Doer, nil)
|
go func() {
|
||||||
ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
|
if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), ctx.Doer.ID); err != nil {
|
||||||
} else {
|
log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err)
|
||||||
ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
|
}
|
||||||
|
}()
|
||||||
|
ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_branch.started"))
|
||||||
|
default:
|
||||||
|
task := cron.GetTask(form.Op)
|
||||||
|
if task != nil {
|
||||||
|
go task.RunWithUser(ctx.Doer, nil)
|
||||||
|
ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if form.From == "monitor" {
|
if form.From == "monitor" {
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -28,32 +27,16 @@ import (
|
|||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
release_service "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
files_service "code.gitea.io/gitea/services/repository/files"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tplBranch base.TplName = "repo/branch/list"
|
tplBranch base.TplName = "repo/branch/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Branch contains the branch information
|
|
||||||
type Branch struct {
|
|
||||||
Name string
|
|
||||||
Commit *git.Commit
|
|
||||||
IsProtected bool
|
|
||||||
IsDeleted bool
|
|
||||||
IsIncluded bool
|
|
||||||
DeletedBranch *git_model.DeletedBranch
|
|
||||||
CommitsAhead int
|
|
||||||
CommitsBehind int
|
|
||||||
LatestPullRequest *issues_model.PullRequest
|
|
||||||
MergeMovedOn bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Branches render repository branch page
|
// Branches render repository branch page
|
||||||
func Branches(ctx *context.Context) {
|
func Branches(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = "Branches"
|
ctx.Data["Title"] = "Branches"
|
||||||
ctx.Data["IsRepoToolbarBranches"] = true
|
ctx.Data["IsRepoToolbarBranches"] = true
|
||||||
ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
|
|
||||||
ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls()
|
ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls()
|
||||||
ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
|
ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
|
||||||
ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
|
ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
|
||||||
@ -68,15 +51,15 @@ func Branches(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
pageSize := setting.Git.BranchesRangeSize
|
pageSize := setting.Git.BranchesRangeSize
|
||||||
|
|
||||||
skip := (page - 1) * pageSize
|
defaultBranch, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, util.OptionalBoolNone, page, pageSize)
|
||||||
log.Debug("Branches: skip: %d limit: %d", skip, pageSize)
|
if err != nil {
|
||||||
defaultBranchBranch, branches, branchesCount := loadBranches(ctx, skip, pageSize)
|
ctx.ServerError("LoadBranches", err)
|
||||||
if ctx.Written() {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Branches"] = branches
|
ctx.Data["Branches"] = branches
|
||||||
ctx.Data["DefaultBranchBranch"] = defaultBranchBranch
|
ctx.Data["DefaultBranchBranch"] = defaultBranch
|
||||||
pager := context.NewPagination(branchesCount, pageSize, page, 5)
|
pager := context.NewPagination(int(branchesCount), pageSize, page, 5)
|
||||||
pager.SetDefaultParams(ctx)
|
pager.SetDefaultParams(ctx)
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
|
|
||||||
@ -130,7 +113,7 @@ func RestoreBranchPost(ctx *context.Context) {
|
|||||||
|
|
||||||
if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{
|
if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{
|
||||||
Remote: ctx.Repo.Repository.RepoPath(),
|
Remote: ctx.Repo.Repository.RepoPath(),
|
||||||
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.Commit, git.BranchPrefix, deletedBranch.Name),
|
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.CommitID, git.BranchPrefix, deletedBranch.Name),
|
||||||
Env: repo_module.PushingEnvironment(ctx.Doer, ctx.Repo.Repository),
|
Env: repo_module.PushingEnvironment(ctx.Doer, ctx.Repo.Repository),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if strings.Contains(err.Error(), "already exists") {
|
if strings.Contains(err.Error(), "already exists") {
|
||||||
@ -148,7 +131,7 @@ func RestoreBranchPost(ctx *context.Context) {
|
|||||||
&repo_module.PushUpdateOptions{
|
&repo_module.PushUpdateOptions{
|
||||||
RefFullName: git.RefNameFromBranch(deletedBranch.Name),
|
RefFullName: git.RefNameFromBranch(deletedBranch.Name),
|
||||||
OldCommitID: git.EmptySHA,
|
OldCommitID: git.EmptySHA,
|
||||||
NewCommitID: deletedBranch.Commit,
|
NewCommitID: deletedBranch.CommitID,
|
||||||
PusherID: ctx.Doer.ID,
|
PusherID: ctx.Doer.ID,
|
||||||
PusherName: ctx.Doer.Name,
|
PusherName: ctx.Doer.Name,
|
||||||
RepoUserName: ctx.Repo.Owner.Name,
|
RepoUserName: ctx.Repo.Owner.Name,
|
||||||
@ -166,180 +149,6 @@ func redirect(ctx *context.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadBranches loads branches from the repository limited by page & pageSize.
|
|
||||||
// NOTE: May write to context on error.
|
|
||||||
func loadBranches(ctx *context.Context, skip, limit int) (*Branch, []*Branch, int) {
|
|
||||||
defaultBranch, err := ctx.Repo.GitRepo.GetBranch(ctx.Repo.Repository.DefaultBranch)
|
|
||||||
if err != nil {
|
|
||||||
if !git.IsErrBranchNotExist(err) {
|
|
||||||
log.Error("loadBranches: get default branch: %v", err)
|
|
||||||
ctx.ServerError("GetDefaultBranch", err)
|
|
||||||
return nil, nil, 0
|
|
||||||
}
|
|
||||||
log.Warn("loadBranches: missing default branch %s for %-v", ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository)
|
|
||||||
}
|
|
||||||
|
|
||||||
rawBranches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(skip, limit)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("GetBranches: %v", err)
|
|
||||||
ctx.ServerError("GetBranches", err)
|
|
||||||
return nil, nil, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("FindRepoProtectedBranchRules", err)
|
|
||||||
return nil, nil, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
repoIDToRepo := map[int64]*repo_model.Repository{}
|
|
||||||
repoIDToRepo[ctx.Repo.Repository.ID] = ctx.Repo.Repository
|
|
||||||
|
|
||||||
repoIDToGitRepo := map[int64]*git.Repository{}
|
|
||||||
repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo
|
|
||||||
|
|
||||||
var branches []*Branch
|
|
||||||
for i := range rawBranches {
|
|
||||||
if defaultBranch != nil && rawBranches[i].Name == defaultBranch.Name {
|
|
||||||
// Skip default branch
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
branch := loadOneBranch(ctx, rawBranches[i], defaultBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
|
||||||
if branch == nil {
|
|
||||||
return nil, nil, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
branches = append(branches, branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultBranchBranch *Branch
|
|
||||||
if defaultBranch != nil {
|
|
||||||
// Always add the default branch
|
|
||||||
log.Debug("loadOneBranch: load default: '%s'", defaultBranch.Name)
|
|
||||||
defaultBranchBranch = loadOneBranch(ctx, defaultBranch, defaultBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
|
||||||
branches = append(branches, defaultBranchBranch)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Repo.CanWrite(unit.TypeCode) {
|
|
||||||
deletedBranches, err := getDeletedBranches(ctx)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("getDeletedBranches", err)
|
|
||||||
return nil, nil, 0
|
|
||||||
}
|
|
||||||
branches = append(branches, deletedBranches...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultBranchBranch, branches, totalNumOfBranches
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadOneBranch(ctx *context.Context, rawBranch, defaultBranch *git.Branch, protectedBranches *git_model.ProtectedBranchRules,
|
|
||||||
repoIDToRepo map[int64]*repo_model.Repository,
|
|
||||||
repoIDToGitRepo map[int64]*git.Repository,
|
|
||||||
) *Branch {
|
|
||||||
log.Trace("loadOneBranch: '%s'", rawBranch.Name)
|
|
||||||
|
|
||||||
commit, err := rawBranch.GetCommit()
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetCommit", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
branchName := rawBranch.Name
|
|
||||||
p := protectedBranches.GetFirstMatched(branchName)
|
|
||||||
isProtected := p != nil
|
|
||||||
|
|
||||||
divergence := &git.DivergeObject{
|
|
||||||
Ahead: -1,
|
|
||||||
Behind: -1,
|
|
||||||
}
|
|
||||||
if defaultBranch != nil {
|
|
||||||
divergence, err = files_service.CountDivergingCommits(ctx, ctx.Repo.Repository, git.BranchPrefix+branchName)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("CountDivergingCommits", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pr, err := issues_model.GetLatestPullRequestByHeadInfo(ctx.Repo.Repository.ID, branchName)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetLatestPullRequestByHeadInfo", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
headCommit := commit.ID.String()
|
|
||||||
|
|
||||||
mergeMovedOn := false
|
|
||||||
if pr != nil {
|
|
||||||
pr.HeadRepo = ctx.Repo.Repository
|
|
||||||
if err := pr.LoadIssue(ctx); err != nil {
|
|
||||||
ctx.ServerError("LoadIssue", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok {
|
|
||||||
pr.BaseRepo = repo
|
|
||||||
} else if err := pr.LoadBaseRepo(ctx); err != nil {
|
|
||||||
ctx.ServerError("LoadBaseRepo", err)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
|
||||||
}
|
|
||||||
pr.Issue.Repo = pr.BaseRepo
|
|
||||||
|
|
||||||
if pr.HasMerged {
|
|
||||||
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
|
||||||
if !ok {
|
|
||||||
baseGitRepo, err = git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("OpenRepository", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer baseGitRepo.Close()
|
|
||||||
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
|
|
||||||
}
|
|
||||||
pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
|
|
||||||
if err != nil && !git.IsErrNotExist(err) {
|
|
||||||
ctx.ServerError("GetBranchCommitID", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err == nil && headCommit != pullCommit {
|
|
||||||
// the head has moved on from the merge - we shouldn't delete
|
|
||||||
mergeMovedOn = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
|
||||||
return &Branch{
|
|
||||||
Name: branchName,
|
|
||||||
Commit: commit,
|
|
||||||
IsProtected: isProtected,
|
|
||||||
IsIncluded: isIncluded,
|
|
||||||
CommitsAhead: divergence.Ahead,
|
|
||||||
CommitsBehind: divergence.Behind,
|
|
||||||
LatestPullRequest: pr,
|
|
||||||
MergeMovedOn: mergeMovedOn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDeletedBranches(ctx *context.Context) ([]*Branch, error) {
|
|
||||||
branches := []*Branch{}
|
|
||||||
|
|
||||||
deletedBranches, err := git_model.GetDeletedBranches(ctx, ctx.Repo.Repository.ID)
|
|
||||||
if err != nil {
|
|
||||||
return branches, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range deletedBranches {
|
|
||||||
deletedBranches[i].LoadUser(ctx)
|
|
||||||
branches = append(branches, &Branch{
|
|
||||||
Name: deletedBranches[i].Name,
|
|
||||||
IsDeleted: true,
|
|
||||||
DeletedBranch: deletedBranches[i],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return branches, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateBranch creates new branch in repository
|
// CreateBranch creates new branch in repository
|
||||||
func CreateBranch(ctx *context.Context) {
|
func CreateBranch(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.NewBranchForm)
|
form := web.GetForm(ctx).(*forms.NewBranchForm)
|
||||||
@ -380,13 +189,13 @@ func CreateBranch(ctx *context.Context) {
|
|||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
|
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrBranchNameConflict(err) {
|
if git_model.IsErrBranchNameConflict(err) {
|
||||||
e := err.(models.ErrBranchNameConflict)
|
e := err.(git_model.ErrBranchNameConflict)
|
||||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
|
ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
@ -124,9 +125,9 @@ func CherryPickPost(ctx *context.Context) {
|
|||||||
// First lets try the simple plain read-tree -m approach
|
// First lets try the simple plain read-tree -m approach
|
||||||
opts.Content = sha
|
opts.Content = sha
|
||||||
if _, err := files.CherryPick(ctx, ctx.Repo.Repository, ctx.Doer, form.Revert, opts); err != nil {
|
if _, err := files.CherryPick(ctx, ctx.Repo.Repository, ctx.Doer, form.Revert, opts); err != nil {
|
||||||
if models.IsErrBranchAlreadyExists(err) {
|
if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
// User has specified a branch that already exists
|
// User has specified a branch that already exists
|
||||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
||||||
return
|
return
|
||||||
@ -161,9 +162,9 @@ func CherryPickPost(ctx *context.Context) {
|
|||||||
ctx.Data["FileContent"] = opts.Content
|
ctx.Data["FileContent"] = opts.Content
|
||||||
|
|
||||||
if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
|
if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
|
||||||
if models.IsErrBranchAlreadyExists(err) {
|
if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
// User has specified a branch that already exists
|
// User has specified a branch that already exists
|
||||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
||||||
return
|
return
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
@ -683,7 +684,13 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor
|
|||||||
}
|
}
|
||||||
defer gitRepo.Close()
|
defer gitRepo.Close()
|
||||||
|
|
||||||
branches, _, err = gitRepo.GetBranchNames(0, 0)
|
branches, err = git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -734,7 +741,13 @@ func CompareDiff(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
headBranches, _, err := ci.HeadGitRepo.GetBranchNames(0, 0)
|
headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||||
|
RepoID: ci.HeadRepo.ID,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranches", err)
|
ctx.ServerError("GetBranches", err)
|
||||||
return
|
return
|
||||||
|
@ -327,10 +327,10 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else if models.IsErrBranchAlreadyExists(err) {
|
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
// For when a user specifies a new branch that already exists
|
// For when a user specifies a new branch that already exists
|
||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
if branchErr, ok := err.(models.ErrBranchAlreadyExists); ok {
|
if branchErr, ok := err.(git_model.ErrBranchAlreadyExists); ok {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
@ -529,9 +529,9 @@ func DeleteFilePost(ctx *context.Context) {
|
|||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else if models.IsErrBranchAlreadyExists(err) {
|
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
// For when a user specifies a new branch that already exists
|
// For when a user specifies a new branch that already exists
|
||||||
if branchErr, ok := err.(models.ErrBranchAlreadyExists); ok {
|
if branchErr, ok := err.(git_model.ErrBranchAlreadyExists); ok {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplDeleteFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplDeleteFile, &form)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
@ -731,10 +731,10 @@ func UploadFilePost(ctx *context.Context) {
|
|||||||
} else if git.IsErrBranchNotExist(err) {
|
} else if git.IsErrBranchNotExist(err) {
|
||||||
branchErr := err.(git.ErrBranchNotExist)
|
branchErr := err.(git.ErrBranchNotExist)
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name), tplUploadFile, &form)
|
||||||
} else if models.IsErrBranchAlreadyExists(err) {
|
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
// For when a user specifies a new branch that already exists
|
// For when a user specifies a new branch that already exists
|
||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
|
||||||
} else if git.IsErrPushOutOfDate(err) {
|
} else if git.IsErrPushOutOfDate(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
|
||||||
|
@ -785,7 +785,13 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
|
brs, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranches", err)
|
ctx.ServerError("GetBranches", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -20,14 +20,12 @@ func LockIssue(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if issue.IsLocked {
|
if issue.IsLocked {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.issues.lock_duplicate"))
|
ctx.JSONError(ctx.Tr("repo.issues.lock_duplicate"))
|
||||||
ctx.Redirect(issue.Link())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !form.HasValidReason() {
|
if !form.HasValidReason() {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.issues.lock.unknown_reason"))
|
ctx.JSONError(ctx.Tr("repo.issues.lock.unknown_reason"))
|
||||||
ctx.Redirect(issue.Link())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +38,7 @@ func LockIssue(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlockIssue unlocks a previously locked issue.
|
// UnlockIssue unlocks a previously locked issue.
|
||||||
@ -51,8 +49,7 @@ func UnlockIssue(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !issue.IsLocked {
|
if !issue.IsLocked {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.issues.unlock_error"))
|
ctx.JSONError(ctx.Tr("repo.issues.unlock_error"))
|
||||||
ctx.Redirect(issue.Link())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,5 +61,5 @@ func UnlockIssue(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func IssuePinOrUnpin(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssueUnpin unpins a Issue
|
// IssueUnpin unpins a Issue
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
@ -94,9 +95,9 @@ func NewDiffPatchPost(ctx *context.Context) {
|
|||||||
Content: strings.ReplaceAll(form.Content, "\r", ""),
|
Content: strings.ReplaceAll(form.Content, "\r", ""),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrBranchAlreadyExists(err) {
|
if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
// User has specified a branch that already exists
|
// User has specified a branch that already exists
|
||||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
branchErr := err.(git_model.ErrBranchAlreadyExists)
|
||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
||||||
return
|
return
|
||||||
|
@ -1493,7 +1493,7 @@ func UpdatePullRequestTarget(ctx *context.Context) {
|
|||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"user_error": errorMessage,
|
"user_error": errorMessage,
|
||||||
})
|
})
|
||||||
} else if models.IsErrBranchesEqual(err) {
|
} else if git_model.IsErrBranchesEqual(err) {
|
||||||
errorMessage := ctx.Tr("repo.pulls.nothing_to_compare")
|
errorMessage := ctx.Tr("repo.pulls.nothing_to_compare")
|
||||||
|
|
||||||
ctx.Flash.Error(errorMessage)
|
ctx.Flash.Error(errorMessage)
|
||||||
|
@ -286,7 +286,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, protectBranch.RuleName)
|
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("FindAllMatchedBranches", err)
|
ctx.ServerError("FindAllMatchedBranches", err)
|
||||||
return
|
return
|
||||||
|
@ -420,7 +420,13 @@ func PackageSettingsPost(ctx *context.Context) {
|
|||||||
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(ctx.Package.Owner.HomeLink() + "/-/packages")
|
redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
|
||||||
|
// redirect to the package if there are still versions available
|
||||||
|
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID}); has {
|
||||||
|
redirectURL = ctx.Package.Descriptor.PackageWebLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(redirectURL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func ToEmailSearch(email *user_model.SearchEmailResult) *api.Email {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
||||||
func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
|
func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName string, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
|
||||||
if bp == nil {
|
if bp == nil {
|
||||||
var hasPerm bool
|
var hasPerm bool
|
||||||
var canPush bool
|
var canPush bool
|
||||||
@ -65,11 +65,11 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
canPush = issues_model.CanMaintainerWriteToBranch(perms, b.Name, user)
|
canPush = issues_model.CanMaintainerWriteToBranch(perms, branchName, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.Branch{
|
return &api.Branch{
|
||||||
Name: b.Name,
|
Name: branchName,
|
||||||
Commit: ToPayloadCommit(ctx, repo, c),
|
Commit: ToPayloadCommit(ctx, repo, c),
|
||||||
Protected: false,
|
Protected: false,
|
||||||
RequiredApprovals: 0,
|
RequiredApprovals: 0,
|
||||||
@ -81,7 +81,7 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
branch := &api.Branch{
|
branch := &api.Branch{
|
||||||
Name: b.Name,
|
Name: branchName,
|
||||||
Commit: ToPayloadCommit(ctx, repo, c),
|
Commit: ToPayloadCommit(ctx, repo, c),
|
||||||
Protected: true,
|
Protected: true,
|
||||||
RequiredApprovals: bp.RequiredApprovals,
|
RequiredApprovals: bp.RequiredApprovals,
|
||||||
|
@ -642,7 +642,7 @@ func (g *RepositoryDumper) Finish() error {
|
|||||||
|
|
||||||
// DumpRepository dump repository according MigrateOptions to a local directory
|
// DumpRepository dump repository according MigrateOptions to a local directory
|
||||||
func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.MigrateOptions) error {
|
func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.MigrateOptions) error {
|
||||||
doer, err := user_model.GetAdminUser()
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -705,7 +705,7 @@ func updateOptionsUnits(opts *base.MigrateOptions, units []string) error {
|
|||||||
|
|
||||||
// RestoreRepository restore a repository from the disk directory
|
// RestoreRepository restore a repository from the disk directory
|
||||||
func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string, units []string, validation bool) error {
|
func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string, units []string, validation bool) error {
|
||||||
doer, err := user_model.GetAdminUser()
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if branchesEqual {
|
if branchesEqual {
|
||||||
return models.ErrBranchesEqual{
|
return git_model.ErrBranchesEqual{
|
||||||
HeadBranchName: pr.HeadBranch,
|
HeadBranchName: pr.HeadBranch,
|
||||||
BaseBranchName: targetBranch,
|
BaseBranchName: targetBranch,
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
|
|||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
divergence, err := GetDiverging(ctx, pr)
|
divergence, err := GetDiverging(ctx, pr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrBranchDoesNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
||||||
log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
|
log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
|
||||||
} else {
|
} else {
|
||||||
log.Error("GetDiverging: %v", err)
|
log.Error("GetDiverging: %v", err)
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
@ -181,7 +181,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
|
|||||||
Run(prCtx.RunOpts()); err != nil {
|
Run(prCtx.RunOpts()); err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
|
||||||
return nil, nil, models.ErrBranchDoesNotExist{
|
return nil, nil, git_model.ErrBranchNotExist{
|
||||||
BranchName: pr.HeadBranch,
|
BranchName: pr.HeadBranch,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
@ -168,7 +167,7 @@ func GetDiverging(ctx context.Context, pr *issues_model.PullRequest) (*git.Diver
|
|||||||
log.Trace("GetDiverging[%-v]: compare commits", pr)
|
log.Trace("GetDiverging[%-v]: compare commits", pr)
|
||||||
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !models.IsErrBranchDoesNotExist(err) {
|
if !git_model.IsErrBranchNotExist(err) {
|
||||||
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
|
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
@ -146,7 +147,15 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
branches, _, _ := gitRepo.GetBranchNames(0, 0)
|
|
||||||
|
branches, _ := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
ListAll: true,
|
||||||
|
},
|
||||||
|
IsDeletedBranch: util.OptionalBoolFalse,
|
||||||
|
})
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
hasDefault := false
|
hasDefault := false
|
||||||
hasMaster := false
|
hasMaster := false
|
||||||
|
@ -10,13 +10,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"code.gitea.io/gitea/modules/notification"
|
||||||
|
"code.gitea.io/gitea/modules/queue"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
files_service "code.gitea.io/gitea/services/repository/files"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateNewBranch creates a new repository branch
|
// CreateNewBranch creates a new repository branch
|
||||||
@ -27,7 +35,7 @@ func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !git.IsBranchExist(ctx, repo.RepoPath(), oldBranchName) {
|
if !git.IsBranchExist(ctx, repo.RepoPath(), oldBranchName) {
|
||||||
return models.ErrBranchDoesNotExist{
|
return git_model.ErrBranchNotExist{
|
||||||
BranchName: oldBranchName,
|
BranchName: oldBranchName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,16 +48,165 @@ func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_mode
|
|||||||
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Push: %w", err)
|
return fmt.Errorf("push: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBranches returns branches from the repository, skipping skip initial branches and
|
// Branch contains the branch information
|
||||||
// returning at most limit branches, or all branches if limit is 0.
|
type Branch struct {
|
||||||
func GetBranches(ctx context.Context, repo *repo_model.Repository, skip, limit int) ([]*git.Branch, int, error) {
|
DBBranch *git_model.Branch
|
||||||
return git.GetBranchesByPath(ctx, repo.RepoPath(), skip, limit)
|
IsProtected bool
|
||||||
|
IsIncluded bool
|
||||||
|
CommitsAhead int
|
||||||
|
CommitsBehind int
|
||||||
|
LatestPullRequest *issues_model.PullRequest
|
||||||
|
MergeMovedOn bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBranches loads branches from the repository limited by page & pageSize.
|
||||||
|
func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, isDeletedBranch util.OptionalBool, page, pageSize int) (*Branch, []*Branch, int64, error) {
|
||||||
|
defaultDBBranch, err := git_model.GetBranch(ctx, repo.ID, repo.DefaultBranch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
branchOpts := git_model.FindBranchOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
IsDeletedBranch: isDeletedBranch,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
Page: page,
|
||||||
|
PageSize: pageSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
totalNumOfBranches, err := git_model.CountBranches(ctx, branchOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
branchOpts.ExcludeBranchNames = []string{repo.DefaultBranch}
|
||||||
|
|
||||||
|
dbBranches, err := git_model.FindBranches(ctx, branchOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dbBranches.LoadDeletedBy(ctx); err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
if err := dbBranches.LoadPusher(ctx); err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rules, err := git_model.FindRepoProtectedBranchRules(ctx, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoIDToRepo := map[int64]*repo_model.Repository{}
|
||||||
|
repoIDToRepo[repo.ID] = repo
|
||||||
|
|
||||||
|
repoIDToGitRepo := map[int64]*git.Repository{}
|
||||||
|
repoIDToGitRepo[repo.ID] = gitRepo
|
||||||
|
|
||||||
|
branches := make([]*Branch, 0, len(dbBranches))
|
||||||
|
for i := range dbBranches {
|
||||||
|
branch, err := loadOneBranch(ctx, repo, dbBranches[i], &rules, repoIDToRepo, repoIDToGitRepo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
branches = append(branches, branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add the default branch
|
||||||
|
log.Debug("loadOneBranch: load default: '%s'", defaultDBBranch.Name)
|
||||||
|
defaultBranch, err := loadOneBranch(ctx, repo, defaultDBBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultBranch, branches, totalNumOfBranches, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *git_model.Branch, protectedBranches *git_model.ProtectedBranchRules,
|
||||||
|
repoIDToRepo map[int64]*repo_model.Repository,
|
||||||
|
repoIDToGitRepo map[int64]*git.Repository,
|
||||||
|
) (*Branch, error) {
|
||||||
|
log.Trace("loadOneBranch: '%s'", dbBranch.Name)
|
||||||
|
|
||||||
|
branchName := dbBranch.Name
|
||||||
|
p := protectedBranches.GetFirstMatched(branchName)
|
||||||
|
isProtected := p != nil
|
||||||
|
|
||||||
|
divergence := &git.DivergeObject{
|
||||||
|
Ahead: -1,
|
||||||
|
Behind: -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's not default branch
|
||||||
|
if repo.DefaultBranch != dbBranch.Name && !dbBranch.IsDeleted {
|
||||||
|
var err error
|
||||||
|
divergence, err = files_service.CountDivergingCommits(ctx, repo, git.BranchPrefix+branchName)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("CountDivergingCommits: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, err := issues_model.GetLatestPullRequestByHeadInfo(repo.ID, branchName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("GetLatestPullRequestByHeadInfo: %v", err)
|
||||||
|
}
|
||||||
|
headCommit := dbBranch.CommitID
|
||||||
|
|
||||||
|
mergeMovedOn := false
|
||||||
|
if pr != nil {
|
||||||
|
pr.HeadRepo = repo
|
||||||
|
if err := pr.LoadIssue(ctx); err != nil {
|
||||||
|
return nil, fmt.Errorf("LoadIssue: %v", err)
|
||||||
|
}
|
||||||
|
if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok {
|
||||||
|
pr.BaseRepo = repo
|
||||||
|
} else if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||||
|
return nil, fmt.Errorf("LoadBaseRepo: %v", err)
|
||||||
|
} else {
|
||||||
|
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
||||||
|
}
|
||||||
|
pr.Issue.Repo = pr.BaseRepo
|
||||||
|
|
||||||
|
if pr.HasMerged {
|
||||||
|
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
||||||
|
if !ok {
|
||||||
|
baseGitRepo, err = git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("OpenRepository: %v", err)
|
||||||
|
}
|
||||||
|
defer baseGitRepo.Close()
|
||||||
|
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
|
||||||
|
}
|
||||||
|
pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
|
||||||
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("GetBranchCommitID: %v", err)
|
||||||
|
}
|
||||||
|
if err == nil && headCommit != pullCommit {
|
||||||
|
// the head has moved on from the merge - we shouldn't delete
|
||||||
|
mergeMovedOn = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isIncluded := divergence.Ahead == 0 && repo.DefaultBranch != branchName
|
||||||
|
return &Branch{
|
||||||
|
DBBranch: dbBranch,
|
||||||
|
IsProtected: isProtected,
|
||||||
|
IsIncluded: isIncluded,
|
||||||
|
CommitsAhead: divergence.Ahead,
|
||||||
|
CommitsBehind: divergence.Behind,
|
||||||
|
LatestPullRequest: pr,
|
||||||
|
MergeMovedOn: mergeMovedOn,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBranchCommitID(ctx context.Context, repo *repo_model.Repository, branch string) (string, error) {
|
func GetBranchCommitID(ctx context.Context, repo *repo_model.Repository, branch string) (string, error) {
|
||||||
@ -62,17 +219,17 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri
|
|||||||
branchRefName := strings.TrimPrefix(refName, git.BranchPrefix)
|
branchRefName := strings.TrimPrefix(refName, git.BranchPrefix)
|
||||||
switch {
|
switch {
|
||||||
case branchRefName == name:
|
case branchRefName == name:
|
||||||
return models.ErrBranchAlreadyExists{
|
return git_model.ErrBranchAlreadyExists{
|
||||||
BranchName: name,
|
BranchName: name,
|
||||||
}
|
}
|
||||||
// If branchRefName like a/b but we want to create a branch named a then we have a conflict
|
// If branchRefName like a/b but we want to create a branch named a then we have a conflict
|
||||||
case strings.HasPrefix(branchRefName, name+"/"):
|
case strings.HasPrefix(branchRefName, name+"/"):
|
||||||
return models.ErrBranchNameConflict{
|
return git_model.ErrBranchNameConflict{
|
||||||
BranchName: branchRefName,
|
BranchName: branchRefName,
|
||||||
}
|
}
|
||||||
// Conversely if branchRefName like a but we want to create a branch named a/b then we also have a conflict
|
// Conversely if branchRefName like a but we want to create a branch named a/b then we also have a conflict
|
||||||
case strings.HasPrefix(name, branchRefName+"/"):
|
case strings.HasPrefix(name, branchRefName+"/"):
|
||||||
return models.ErrBranchNameConflict{
|
return git_model.ErrBranchNameConflict{
|
||||||
BranchName: branchRefName,
|
BranchName: branchRefName,
|
||||||
}
|
}
|
||||||
case refName == git.TagPrefix+name:
|
case refName == git.TagPrefix+name:
|
||||||
@ -101,7 +258,7 @@ func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo
|
|||||||
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Push: %w", err)
|
return fmt.Errorf("push: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -169,13 +326,28 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
|
|||||||
return git_model.ErrBranchIsProtected
|
return git_model.ErrBranchIsProtected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rawBranch, err := git_model.GetBranch(ctx, repo.ID, branchName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetBranch: %vc", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rawBranch.IsDeleted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
commit, err := gitRepo.GetBranchCommit(branchName)
|
commit, err := gitRepo.GetBranchCommit(branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
Force: true,
|
if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
|
||||||
|
Force: true,
|
||||||
|
})
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -196,3 +368,45 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchSyncOptions struct {
|
||||||
|
RepoID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// branchSyncQueue represents a queue to handle branch sync jobs.
|
||||||
|
var branchSyncQueue *queue.WorkerPoolQueue[*BranchSyncOptions]
|
||||||
|
|
||||||
|
func handlerBranchSync(items ...*BranchSyncOptions) []*BranchSyncOptions {
|
||||||
|
for _, opts := range items {
|
||||||
|
_, err := repo_module.SyncRepoBranches(graceful.GetManager().ShutdownContext(), opts.RepoID, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("syncRepoBranches [%d] failed: %v", opts.RepoID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRepoToBranchSyncQueue(repoID, doerID int64) error {
|
||||||
|
return branchSyncQueue.Push(&BranchSyncOptions{
|
||||||
|
RepoID: repoID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func initBranchSyncQueue(ctx context.Context) error {
|
||||||
|
branchSyncQueue = queue.CreateUniqueQueue(ctx, "branch_sync", handlerBranchSync)
|
||||||
|
if branchSyncQueue == nil {
|
||||||
|
return errors.New("unable to create branch_sync queue")
|
||||||
|
}
|
||||||
|
go graceful.GetManager().RunWithCancel(branchSyncQueue)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddAllRepoBranchesToSyncQueue(ctx context.Context, doerID int64) error {
|
||||||
|
if err := db.Iterate(ctx, builder.Eq{"is_empty": false}, func(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
|
return addRepoToBranchSyncQueue(repo.ID, doerID)
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("run sync all branches failed: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -58,7 +58,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode
|
|||||||
if opts.NewBranch != opts.OldBranch {
|
if opts.NewBranch != opts.OldBranch {
|
||||||
existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
|
existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
|
||||||
if existingBranch != nil {
|
if existingBranch != nil {
|
||||||
return models.ErrBranchAlreadyExists{
|
return git_model.ErrBranchAlreadyExists{
|
||||||
BranchName: opts.NewBranch,
|
BranchName: opts.NewBranch,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
|||||||
if opts.NewBranch != opts.OldBranch {
|
if opts.NewBranch != opts.OldBranch {
|
||||||
existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
|
existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
|
||||||
if existingBranch != nil {
|
if existingBranch != nil {
|
||||||
return nil, models.ErrBranchAlreadyExists{
|
return nil, git_model.ErrBranchAlreadyExists{
|
||||||
BranchName: opts.NewBranch,
|
BranchName: opts.NewBranch,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,15 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
|||||||
if err = repo_module.CreateDelegateHooks(repoPath); err != nil {
|
if err = repo_module.CreateDelegateHooks(repoPath); err != nil {
|
||||||
return fmt.Errorf("createDelegateHooks: %w", err)
|
return fmt.Errorf("createDelegateHooks: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
gitRepo, err := git.OpenRepository(txCtx, repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("OpenRepository: %w", err)
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
_, err = repo_module.SyncRepoBranchesWithRepo(txCtx, repo, gitRepo, doer.ID)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
needsRollbackInPanic = false
|
needsRollbackInPanic = false
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,7 +93,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
|||||||
defer gitRepo.Close()
|
defer gitRepo.Close()
|
||||||
|
|
||||||
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
|
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
|
||||||
log.Error("Failed to update size for repository: %v", err)
|
return fmt.Errorf("Failed to update size for repository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
addTags := make([]string, 0, len(optsList))
|
addTags := make([]string, 0, len(optsList))
|
||||||
@ -259,8 +259,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
|||||||
|
|
||||||
notification.NotifyPushCommits(ctx, pusher, repo, opts, commits)
|
notification.NotifyPushCommits(ctx, pusher, repo, opts, commits)
|
||||||
|
|
||||||
if err = git_model.RemoveDeletedBranchByName(ctx, repo.ID, branch); err != nil {
|
if err = git_model.UpdateBranch(ctx, repo.ID, branch, newCommit.ID.String(), newCommit.CommitMessage, opts.PusherID, newCommit.Committer.When); err != nil {
|
||||||
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branch, err)
|
return fmt.Errorf("git_model.UpdateBranch %s:%s failed: %v", repo.FullName(), branch, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache for big repository
|
// Cache for big repository
|
||||||
@ -273,8 +273,9 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
|||||||
// close all related pulls
|
// close all related pulls
|
||||||
log.Error("close related pull request failed: %v", err)
|
log.Error("close related pull request failed: %v", err)
|
||||||
}
|
}
|
||||||
if err := git_model.AddDeletedBranch(db.DefaultContext, repo.ID, branch, opts.OldCommitID, pusher.ID); err != nil {
|
|
||||||
log.Warn("AddDeletedBranch: %v", err)
|
if err := git_model.AddDeletedBranch(db.DefaultContext, repo.ID, branch, pusher.ID); err != nil {
|
||||||
|
return fmt.Errorf("AddDeletedBranch %s:%s failed: %v", repo.FullName(), branch, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
system_model "code.gitea.io/gitea/models/system"
|
system_model "code.gitea.io/gitea/models/system"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"code.gitea.io/gitea/modules/notification"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
@ -100,7 +101,10 @@ func Init() error {
|
|||||||
}
|
}
|
||||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
|
system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
|
||||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
|
system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
|
||||||
return initPushQueue()
|
if err := initPushQueue(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return initBranchSyncQueue(graceful.GetManager().ShutdownContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRepository updates a repository
|
// UpdateRepository updates a repository
|
||||||
|
@ -20,19 +20,19 @@
|
|||||||
<dt>{{.locale.Tr "admin.config.disable_router_log"}}</dt>
|
<dt>{{.locale.Tr "admin.config.disable_router_log"}}</dt>
|
||||||
<dd>{{if .DisableRouterLog}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
<dd>{{if .DisableRouterLog}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<dt>{{.locale.Tr "admin.config.run_user"}}</dt>
|
<dt>{{.locale.Tr "admin.config.run_user"}}</dt>
|
||||||
<dd>{{.RunUser}}</dd>
|
<dd>{{.RunUser}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.config.run_mode"}}</dt>
|
<dt>{{.locale.Tr "admin.config.run_mode"}}</dt>
|
||||||
<dd>{{.RunMode}}</dd>
|
<dd>{{.RunMode}}</dd>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<dt>{{.locale.Tr "admin.config.git_version"}}</dt>
|
<dt>{{.locale.Tr "admin.config.git_version"}}</dt>
|
||||||
<dd>{{.GitVersion}}</dd>
|
<dd>{{.GitVersion}}</dd>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<dt>{{.locale.Tr "admin.config.repo_root_path"}}</dt>
|
<dt>{{.locale.Tr "admin.config.repo_root_path"}}</dt>
|
||||||
<dd>{{.RepoRootPath}}</dd>
|
<dd>{{.RepoRootPath}}</dd>
|
||||||
@ -174,7 +174,7 @@
|
|||||||
<dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd>
|
<dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.config.default_enable_dependencies"}}</dt>
|
<dt>{{.locale.Tr "admin.config.default_enable_dependencies"}}</dt>
|
||||||
<dd>{{if .Service.DefaultEnableDependencies}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
<dd>{{if .Service.DefaultEnableDependencies}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt>{{.locale.Tr "admin.config.active_code_lives"}}</dt>
|
<dt>{{.locale.Tr "admin.config.active_code_lives"}}</dt>
|
||||||
<dd>{{.Service.ActiveCodeLives}} {{.locale.Tr "tool.raw_minutes"}}</dd>
|
<dd>{{.Service.ActiveCodeLives}} {{.locale.Tr "tool.raw_minutes"}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.config.reset_password_code_lives"}}</dt>
|
<dt>{{.locale.Tr "admin.config.reset_password_code_lives"}}</dt>
|
||||||
@ -230,7 +230,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
<dt>{{.locale.Tr "admin.config.mailer_user"}}</dt>
|
<dt>{{.locale.Tr "admin.config.mailer_user"}}</dt>
|
||||||
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
|
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt class="gt-py-2">{{.locale.Tr "admin.config.send_test_mail"}}</dt>
|
<dt class="gt-py-2">{{.locale.Tr "admin.config.send_test_mail"}}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
|
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
|
||||||
@ -296,7 +296,7 @@
|
|||||||
<input type="checkbox" name="picture.disable_gravatar" version="{{.SystemSettings.GetVersion "picture.disable_gravatar"}}"{{if .SystemSettings.GetBool "picture.disable_gravatar"}} checked{{end}} title="{{.locale.Tr "admin.config.disable_gravatar"}}">
|
<input type="checkbox" name="picture.disable_gravatar" version="{{.SystemSettings.GetVersion "picture.disable_gravatar"}}"{{if .SystemSettings.GetBool "picture.disable_gravatar"}} checked{{end}} title="{{.locale.Tr "admin.config.disable_gravatar"}}">
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt>{{.locale.Tr "admin.config.enable_federated_avatar"}}</dt>
|
<dt>{{.locale.Tr "admin.config.enable_federated_avatar"}}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="ui toggle checkbox">
|
<div class="ui toggle checkbox">
|
||||||
@ -322,7 +322,7 @@
|
|||||||
<dt>{{.locale.Tr "admin.config.git_gc_args"}}</dt>
|
<dt>{{.locale.Tr "admin.config.git_gc_args"}}</dt>
|
||||||
<dd><code>{{.Git.GCArgs}}</code></dd>
|
<dd><code>{{.Git.GCArgs}}</code></dd>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<dt>{{.locale.Tr "admin.config.git_migrate_timeout"}}</dt>
|
<dt>{{.locale.Tr "admin.config.git_migrate_timeout"}}</dt>
|
||||||
<dd>{{.Git.Timeout.Migrate}} {{.locale.Tr "tool.raw_seconds"}}</dd>
|
<dd>{{.Git.Timeout.Migrate}} {{.locale.Tr "tool.raw_seconds"}}</dd>
|
||||||
|
@ -61,6 +61,10 @@
|
|||||||
<td>{{.locale.Tr "admin.dashboard.delete_generated_repository_avatars"}}</td>
|
<td>{{.locale.Tr "admin.dashboard.delete_generated_repository_avatars"}}</td>
|
||||||
<td class="text right"><button type="submit" class="ui green button" name="op" value="delete_generated_repository_avatars">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
|
<td class="text right"><button type="submit" class="ui green button" name="op" value="delete_generated_repository_avatars">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{.locale.Tr "admin.dashboard.sync_repo_branches"}}</td>
|
||||||
|
<td class="text right"><button type="submit" class="ui green button" name="op" value="sync_repo_branches">{{svg "octicon-play"}} {{.locale.Tr "admin.dashboard.operation_run"}}</button></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
@ -75,7 +79,7 @@
|
|||||||
<dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd>
|
<dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.current_goroutine"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.current_goroutine"}}</dt>
|
||||||
<dd>{{.SysStatus.NumGoroutine}}</dd>
|
<dd>{{.SysStatus.NumGoroutine}}</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.current_memory_usage"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.current_memory_usage"}}</dt>
|
||||||
<dd>{{.SysStatus.MemAllocated}}</dd>
|
<dd>{{.SysStatus.MemAllocated}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.total_memory_allocated"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.total_memory_allocated"}}</dt>
|
||||||
@ -88,7 +92,7 @@
|
|||||||
<dd>{{.SysStatus.MemMallocs}}</dd>
|
<dd>{{.SysStatus.MemMallocs}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.memory_free_times"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.memory_free_times"}}</dt>
|
||||||
<dd>{{.SysStatus.MemFrees}}</dd>
|
<dd>{{.SysStatus.MemFrees}}</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.current_heap_usage"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.current_heap_usage"}}</dt>
|
||||||
<dd>{{.SysStatus.HeapAlloc}}</dd>
|
<dd>{{.SysStatus.HeapAlloc}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt>
|
||||||
@ -101,7 +105,7 @@
|
|||||||
<dd>{{.SysStatus.HeapReleased}}</dd>
|
<dd>{{.SysStatus.HeapReleased}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.heap_objects"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.heap_objects"}}</dt>
|
||||||
<dd>{{.SysStatus.HeapObjects}}</dd>
|
<dd>{{.SysStatus.HeapObjects}}</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt>
|
||||||
<dd>{{.SysStatus.StackInuse}}</dd>
|
<dd>{{.SysStatus.StackInuse}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt>
|
||||||
@ -120,7 +124,7 @@
|
|||||||
<dd>{{.SysStatus.GCSys}}</dd>
|
<dd>{{.SysStatus.GCSys}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt>
|
||||||
<dd>{{.SysStatus.OtherSys}}</dd>
|
<dd>{{.SysStatus.OtherSys}}</dd>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.next_gc_recycle"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.next_gc_recycle"}}</dt>
|
||||||
<dd>{{.SysStatus.NextGC}}</dd>
|
<dd>{{.SysStatus.NextGC}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.dashboard.last_gc_time"}}</dt>
|
<dt>{{.locale.Tr "admin.dashboard.last_gc_time"}}</dt>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<h4 class="ui top attached header">
|
<h4 class="ui top attached header">
|
||||||
{{printf "%d Goroutines" .GoroutineCount}}{{/* Goroutine is non-translatable*/}}
|
{{printf "%d Goroutines" .GoroutineCount}}{{/* Goroutine is non-translatable*/}}
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
<input id="location" name="location" value="{{.User.Location}}" maxlength="50">
|
<input id="location" name="location" value="{{.User.Location}}" maxlength="50">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
|
<div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
|
||||||
<label for="max_repo_creation">{{.locale.Tr "admin.users.max_repo_creation"}}</label>
|
<label for="max_repo_creation">{{.locale.Tr "admin.users.max_repo_creation"}}</label>
|
||||||
@ -87,7 +87,7 @@
|
|||||||
<p class="help">{{.locale.Tr "admin.users.max_repo_creation_desc"}}</p>
|
<p class="help">{{.locale.Tr "admin.users.max_repo_creation_desc"}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
@ -135,7 +135,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .TwoFactorEnabled}}
|
{{if .TwoFactorEnabled}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label><strong>{{.locale.Tr "admin.users.reset_2fa"}}</strong></label>
|
<label><strong>{{.locale.Tr "admin.users.reset_2fa"}}</strong></label>
|
||||||
@ -144,7 +144,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<button class="ui green button">{{.locale.Tr "admin.users.update_profile"}}</button>
|
<button class="ui green button">{{.locale.Tr "admin.users.update_profile"}}</button>
|
||||||
|
@ -16,19 +16,19 @@
|
|||||||
<span class="text">{{.locale.Tr "admin.users.list_status_filter.menu_text"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span>
|
<span class="text">{{.locale.Tr "admin.users.list_status_filter.menu_text"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<a class="item j-reset-status-filter">{{.locale.Tr "admin.users.list_status_filter.reset"}}</a>
|
<a class="item j-reset-status-filter">{{.locale.Tr "admin.users.list_status_filter.reset"}}</a>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_admin]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_admin"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_admin]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_admin"}}</label>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_admin]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_admin"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_admin]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_admin"}}</label>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_active]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_active"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_active]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_active"}}</label>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_active]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_active"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_active]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_active"}}</label>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_restricted]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_restricted"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_restricted]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_restricted"}}</label>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_restricted]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_restricted"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_restricted]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_restricted"}}</label>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_prohibit_login]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_prohibit_login"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_prohibit_login]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_prohibit_login"}}</label>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_prohibit_login]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_prohibit_login"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_prohibit_login]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_prohibit_login"}}</label>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_2fa_enabled]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_2fa_enabled"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_2fa_enabled]" value="1"> {{.locale.Tr "admin.users.list_status_filter.is_2fa_enabled"}}</label>
|
||||||
<label class="item"><input type="radio" name="status_filter[is_2fa_enabled]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_2fa_enabled"}}</label>
|
<label class="item"><input type="radio" name="status_filter[is_2fa_enabled]" value="0"> {{.locale.Tr "admin.users.list_status_filter.not_2fa_enabled"}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{{template "explore/navbar" .}}
|
{{template "explore/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "code/searchform" .}}
|
{{template "code/searchform" .}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui user list">
|
<div class="ui user list">
|
||||||
{{if .CodeIndexerUnavailable}}
|
{{if .CodeIndexerUnavailable}}
|
||||||
<div class="ui error message">
|
<div class="ui error message">
|
||||||
|
@ -39,4 +39,4 @@
|
|||||||
<span data-tooltip-content="{{.locale.Tr "explore.relevant_repositories_tooltip"}}">{{.locale.Tr "explore.relevant_repositories" ((printf "%s%s" $.Link "?only_show_relevant=0")|Escape) | Safe}}</span>
|
<span data-tooltip-content="{{.locale.Tr "explore.relevant_repositories_tooltip"}}">{{.locale.Tr "explore.relevant_repositories" ((printf "%s%s" $.Link "?only_show_relevant=0")|Escape) | Safe}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
@ -21,4 +21,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
@ -321,7 +321,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label></label>
|
<label></label>
|
||||||
<button class="ui primary button">{{.locale.Tr "install.install_btn_confirm"}}</button>
|
<button class="ui primary button">{{.locale.Tr "install.install_btn_confirm"}}</button>
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<a class="ui green button" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{.locale.Tr "new_migrate"}}</a>
|
<a class="ui green button" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{.locale.Tr "new_migrate"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<h4 class="ui top attached header gt-df">
|
<h4 class="ui top attached header gt-df">
|
||||||
<strong class="gt-f1">{{.locale.Tr "org.members"}}</strong>
|
<strong class="gt-f1">{{.locale.Tr "org.members"}}</strong>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{template "repo/issue/labels/label_new" .}}
|
{{template "repo/issue/labels/label_new" .}}
|
||||||
{{template "repo/issue/labels/label_list" .}}
|
{{template "repo/issue/labels/label_list" .}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<input id="location" name="location" value="{{.Org.Location}}" maxlength="50">
|
<input id="location" name="location" value="{{.Org.Location}}" maxlength="50">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="field" id="visibility_box">
|
<div class="field" id="visibility_box">
|
||||||
<label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label>
|
<label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .SignedUser.IsAdmin}}
|
{{if .SignedUser.IsAdmin}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
|
<div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
|
||||||
<label for="max_repo_creation">{{.locale.Tr "admin.users.max_repo_creation"}}</label>
|
<label for="max_repo_creation">{{.locale.Tr "admin.users.max_repo_creation"}}</label>
|
||||||
@ -79,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="team-units required grouped field {{if eq .Team.AccessMode 3}}gt-hidden{{end}}">
|
<div class="team-units required grouped field {{if eq .Team.AccessMode 3}}gt-hidden{{end}}">
|
||||||
<label>{{.locale.Tr "org.team_unit_desc"}}</label>
|
<label>{{.locale.Tr "org.team_unit_desc"}}</label>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="text right">
|
<div class="text right">
|
||||||
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus"}} {{.locale.Tr "org.create_new_team"}}</a>
|
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus"}} {{.locale.Tr "org.create_new_team"}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="ui two column stackable grid">
|
<div class="ui two column stackable grid">
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<input type="checkbox" name="match_full_name" {{if .CleanupRule.MatchFullName}}checked{{end}}>
|
<input type="checkbox" name="match_full_name" {{if .CleanupRule.MatchFullName}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<p>{{.locale.Tr "packages.owner.settings.cleanuprules.keep.title"}}</p>
|
<p>{{.locale.Tr "packages.owner.settings.cleanuprules.keep.title"}}</p>
|
||||||
<div class="field {{if .Err_KeepCount}}error{{end}}">
|
<div class="field {{if .Err_KeepCount}}error{{end}}">
|
||||||
<label>{{.locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}:</label>
|
<label>{{.locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}:</label>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<input name="keep_pattern" type="text" value="{{.CleanupRule.KeepPattern}}">
|
<input name="keep_pattern" type="text" value="{{.CleanupRule.KeepPattern}}">
|
||||||
<p>{{.locale.Tr "packages.owner.settings.cleanuprules.keep.pattern.container" | Safe}}</p>
|
<p>{{.locale.Tr "packages.owner.settings.cleanuprules.keep.pattern.container" | Safe}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<p>{{.locale.Tr "packages.owner.settings.cleanuprules.remove.title"}}</p>
|
<p>{{.locale.Tr "packages.owner.settings.cleanuprules.remove.title"}}</p>
|
||||||
<div class="field {{if .Err_RemoveDays}}error{{end}}">
|
<div class="field {{if .Err_RemoveDays}}error{{end}}">
|
||||||
<label>{{.locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}:</label>
|
<label>{{.locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}:</label>
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if not (eq .PackageDescriptor.Package.Type "container")}}
|
{{if not (eq .PackageDescriptor.Package.Type "container")}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<strong>{{.locale.Tr "packages.assets"}} ({{len .PackageDescriptor.Files}})</strong>
|
<strong>{{.locale.Tr "packages.assets"}} ({{len .PackageDescriptor.Files}})</strong>
|
||||||
<div class="ui relaxed list">
|
<div class="ui relaxed list">
|
||||||
{{range .PackageDescriptor.Files}}
|
{{range .PackageDescriptor.Files}}
|
||||||
@ -84,21 +84,19 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .LatestVersions}}
|
<div class="divider"></div>
|
||||||
<div class="ui divider"></div>
|
<strong>{{.locale.Tr "packages.versions"}} ({{.TotalVersionCount}})</strong>
|
||||||
<strong>{{.locale.Tr "packages.versions"}} ({{.TotalVersionCount}})</strong>
|
<a class="ui right" href="{{$.PackageDescriptor.PackageWebLink}}/versions">{{.locale.Tr "packages.versions.view_all"}}</a>
|
||||||
<a class="ui right" href="{{$.PackageDescriptor.PackageWebLink}}/versions">{{.locale.Tr "packages.versions.view_all"}}</a>
|
<div class="ui relaxed list">
|
||||||
<div class="ui relaxed list">
|
{{range .LatestVersions}}
|
||||||
{{range .LatestVersions}}
|
<div class="item gt-df">
|
||||||
<div class="item gt-df">
|
<a class="gt-f1" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
|
||||||
<a class="gt-f1" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
|
<span class="text small">{{DateTime "short" .CreatedUnix}}</span>
|
||||||
<span class="text small">{{DateTime "short" .CreatedUnix}}</span>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
</div>
|
||||||
{{if or .CanWritePackages .HasRepositoryAccess}}
|
{{if or .CanWritePackages .HasRepositoryAccess}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui relaxed list">
|
<div class="ui relaxed list">
|
||||||
{{if .HasRepositoryAccess}}
|
{{if .HasRepositoryAccess}}
|
||||||
<div class="item">{{svg "octicon-issue-opened" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{.locale.Tr "repo.issues"}}</a></div>
|
<div class="item">{{svg "octicon-issue-opened" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{.locale.Tr "repo.issues"}}</a></div>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a>
|
<a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui left">
|
<div class="ui left">
|
||||||
<a class="ui cancel button" href="{{$.CancelLink}}">
|
<a class="ui cancel button" href="{{$.CancelLink}}">
|
||||||
{{.locale.Tr "repo.milestones.cancel"}}
|
{{.locale.Tr "repo.milestones.cancel"}}
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui two column stackable grid">
|
<div class="ui two column stackable grid">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h2 class="project-title">{{$.Project.Title}}</h2>
|
<h2 class="project-title">{{$.Project.Title}}</h2>
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui container fluid padded" id="project-board">
|
<div class="ui container fluid padded" id="project-board">
|
||||||
|
|
||||||
@ -175,7 +175,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="ui cards board" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
|
<div class="ui cards board" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}
|
{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}
|
||||||
<h4 class="ui top attached header">{{.locale.Tr "repo.activity.overview"}}</h4>
|
<h4 class="ui top attached header">{{.locale.Tr "repo.activity.overview"}}</h4>
|
||||||
@ -115,8 +115,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt .Activity.PublishedReleaseCount 0}}
|
{{if gt .Activity.PublishedReleaseCount 0}}
|
||||||
<h4 class="ui horizontal divider header" id="published-releases">
|
<h4 class="divider divider-text gt-normal-case" id="published-releases">
|
||||||
<span class="text">{{svg "octicon-tag"}}</span>
|
{{svg "octicon-tag" 16 "gt-mr-3"}}
|
||||||
{{.locale.Tr "repo.activity.title.releases_published_by"
|
{{.locale.Tr "repo.activity.title.releases_published_by"
|
||||||
(.locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount)
|
(.locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount)
|
||||||
(.locale.TrN .Activity.PublishedReleaseAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.PublishedReleaseAuthorCount)
|
(.locale.TrN .Activity.PublishedReleaseAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.PublishedReleaseAuthorCount)
|
||||||
@ -137,8 +137,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt .Activity.MergedPRCount 0}}
|
{{if gt .Activity.MergedPRCount 0}}
|
||||||
<h4 class="ui horizontal divider header" id="merged-pull-requests">
|
<h4 class="divider divider-text gt-normal-case" id="merged-pull-requests">
|
||||||
<span class="text">{{svg "octicon-git-pull-request"}}</span>
|
{{svg "octicon-git-pull-request" 16 "gt-mr-3"}}
|
||||||
{{.locale.Tr "repo.activity.title.prs_merged_by"
|
{{.locale.Tr "repo.activity.title.prs_merged_by"
|
||||||
(.locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount)
|
(.locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount)
|
||||||
(.locale.TrN .Activity.MergedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.MergedPRAuthorCount)
|
(.locale.TrN .Activity.MergedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.MergedPRAuthorCount)
|
||||||
@ -156,8 +156,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt .Activity.OpenedPRCount 0}}
|
{{if gt .Activity.OpenedPRCount 0}}
|
||||||
<h4 class="ui horizontal divider header" id="proposed-pull-requests">
|
<h4 class="divider divider-text gt-normal-case" id="proposed-pull-requests">
|
||||||
<span class="text">{{svg "octicon-git-branch"}}</span>
|
{{svg "octicon-git-branch" 16 "gt-mr-3"}}
|
||||||
{{.locale.Tr "repo.activity.title.prs_opened_by"
|
{{.locale.Tr "repo.activity.title.prs_opened_by"
|
||||||
(.locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount)
|
(.locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount)
|
||||||
(.locale.TrN .Activity.OpenedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedPRAuthorCount)
|
(.locale.TrN .Activity.OpenedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedPRAuthorCount)
|
||||||
@ -175,8 +175,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt .Activity.ClosedIssueCount 0}}
|
{{if gt .Activity.ClosedIssueCount 0}}
|
||||||
<h4 class="ui horizontal divider header" id="closed-issues">
|
<h4 class="divider divider-text gt-normal-case" id="closed-issues">
|
||||||
<span class="text">{{svg "octicon-issue-closed"}}</span>
|
{{svg "octicon-issue-closed" 16 "gt-mr-3"}}
|
||||||
{{.locale.Tr "repo.activity.title.issues_closed_from"
|
{{.locale.Tr "repo.activity.title.issues_closed_from"
|
||||||
(.locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount)
|
(.locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount)
|
||||||
(.locale.TrN .Activity.ClosedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.ClosedIssueAuthorCount)
|
(.locale.TrN .Activity.ClosedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.ClosedIssueAuthorCount)
|
||||||
@ -194,8 +194,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt .Activity.OpenedIssueCount 0}}
|
{{if gt .Activity.OpenedIssueCount 0}}
|
||||||
<h4 class="ui horizontal divider header" id="new-issues">
|
<h4 class="divider divider-text gt-normal-case" id="new-issues">
|
||||||
<span class="text">{{svg "octicon-issue-opened"}}</span>
|
{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
|
||||||
{{.locale.Tr "repo.activity.title.issues_created_by"
|
{{.locale.Tr "repo.activity.title.issues_created_by"
|
||||||
(.locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount)
|
(.locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount)
|
||||||
(.locale.TrN .Activity.OpenedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedIssueAuthorCount)
|
(.locale.TrN .Activity.OpenedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedIssueAuthorCount)
|
||||||
@ -213,13 +213,10 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt .Activity.UnresolvedIssueCount 0}}
|
{{if gt .Activity.UnresolvedIssueCount 0}}
|
||||||
<h4 class="ui horizontal divider header" id="unresolved-conversations">
|
<h4 class="divider divider-text gt-normal-case" id="unresolved-conversations" data-tooltip-content="{{.locale.Tr "repo.activity.unresolved_conv_desc"}}">
|
||||||
<span class="text">{{svg "octicon-comment-discussion"}}</span>
|
{{svg "octicon-comment-discussion" 16 "gt-mr-3"}}
|
||||||
{{.locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}
|
{{.locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="text center desc">
|
|
||||||
{{.locale.Tr "repo.activity.unresolved_conv_desc"}}
|
|
||||||
</div>
|
|
||||||
<div class="list">
|
<div class="list">
|
||||||
{{range .Activity.UnresolvedIssues}}
|
{{range .Activity.UnresolvedIssues}}
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
|
@ -22,29 +22,29 @@
|
|||||||
{{if .DefaultBranchBranch.IsProtected}}
|
{{if .DefaultBranchBranch.IsProtected}}
|
||||||
{{svg "octicon-shield-lock"}}
|
{{svg "octicon-shield-lock"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<a href="{{.RepoLink}}/src/branch/{{PathEscapeSegments .DefaultBranch}}">{{.DefaultBranch}}</a>
|
<a href="{{.RepoLink}}/src/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}">{{.DefaultBranchBranch.DBBranch.Name}}</a>
|
||||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.Commit.ID.String}}">{{ShortSha .DefaultBranchBranch.Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.Commit.CommitMessage .RepoLink .Repository.ComposeMetas}}</span> · {{.locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.Commit.Committer.When .locale}}</p>
|
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage .RepoLink .Repository.ComposeMetas}}</span> · {{.locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime .locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "Context" $.Context "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="right aligned overflow-visible">
|
<td class="right aligned overflow-visible">
|
||||||
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
|
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
|
||||||
<button class="btn interact-bg show-create-branch-modal gt-p-3"
|
<button class="btn interact-bg show-create-branch-modal gt-p-3"
|
||||||
data-modal="#create-branch-modal"
|
data-modal="#create-branch-modal"
|
||||||
data-branch-from="{{$.DefaultBranch}}"
|
data-branch-from="{{$.DefaultBranchBranch}}"
|
||||||
data-branch-from-urlcomponent="{{PathEscapeSegments $.DefaultBranch}}"
|
data-branch-from-urlcomponent="{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}"
|
||||||
data-tooltip-content="{{$.locale.Tr "repo.branch.new_branch_from" ($.DefaultBranch)}}"
|
data-tooltip-content="{{$.locale.Tr "repo.branch.new_branch_from" ($.DefaultBranchBranch.DBBranch.Name)}}"
|
||||||
>
|
>
|
||||||
{{svg "octicon-git-branch"}}
|
{{svg "octicon-git-branch"}}
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .EnableFeed}}
|
{{if .EnableFeed}}
|
||||||
<a role="button" class="btn interact-bg gt-p-3" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranch}}">{{svg "octicon-rss"}}</a>
|
<a role="button" class="btn interact-bg gt-p-3" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}">{{svg "octicon-rss"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if not $.DisableDownloadSourceArchives}}
|
{{if not $.DisableDownloadSourceArchives}}
|
||||||
<div class="ui dropdown btn interact-bg gt-p-3" data-tooltip-content="{{$.locale.Tr "repo.branch.download" ($.DefaultBranch)}}">
|
<div class="ui dropdown btn interact-bg gt-p-3" data-tooltip-content="{{$.locale.Tr "repo.branch.download" ($.DefaultBranchBranch.DBBranch.Name)}}">
|
||||||
{{svg "octicon-download"}}
|
{{svg "octicon-download"}}
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.DefaultBranch}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} ZIP</a>
|
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} ZIP</a>
|
||||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.DefaultBranch}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} TAR.GZ</a>
|
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} TAR.GZ</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -52,8 +52,8 @@
|
|||||||
<button class="btn interact-bg gt-p-3 show-modal show-rename-branch-modal"
|
<button class="btn interact-bg gt-p-3 show-modal show-rename-branch-modal"
|
||||||
data-is-default-branch="true"
|
data-is-default-branch="true"
|
||||||
data-modal="#rename-branch-modal"
|
data-modal="#rename-branch-modal"
|
||||||
data-old-branch-name="{{$.DefaultBranch}}"
|
data-old-branch-name="{{$.DefaultBranchBranch}}"
|
||||||
data-tooltip-content="{{$.locale.Tr "repo.branch.rename" ($.DefaultBranch)}}"
|
data-tooltip-content="{{$.locale.Tr "repo.branch.rename" ($.DefaultBranchBranch.DBBranch.Name)}}"
|
||||||
>
|
>
|
||||||
{{svg "octicon-pencil"}}
|
{{svg "octicon-pencil"}}
|
||||||
</button>
|
</button>
|
||||||
@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if gt (len .Branches) 1}}
|
{{if .Branches}}
|
||||||
<h4 class="ui top attached header">
|
<h4 class="ui top attached header">
|
||||||
{{.locale.Tr "repo.branches"}}
|
{{.locale.Tr "repo.branches"}}
|
||||||
</h4>
|
</h4>
|
||||||
@ -73,112 +73,110 @@
|
|||||||
<table class="ui very basic striped fixed table single line">
|
<table class="ui very basic striped fixed table single line">
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range .Branches}}
|
{{range .Branches}}
|
||||||
{{if ne .Name $.DefaultBranch}}
|
<tr>
|
||||||
<tr>
|
<td class="eight wide">
|
||||||
<td class="six wide">
|
{{if .DBBranch.IsDeleted}}
|
||||||
{{if .IsDeleted}}
|
<s><a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a></s>
|
||||||
<s><a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .Name}}">{{.Name}}</a></s>
|
<p class="info">{{$.locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{TimeSinceUnix .DBBranch.DeletedUnix $.locale}}</p>
|
||||||
<p class="info">{{$.locale.Tr "repo.branch.deleted_by" .DeletedBranch.DeletedBy.Name}} {{TimeSinceUnix .DeletedBranch.DeletedUnix $.locale}}</p>
|
{{else}}
|
||||||
{{else}}
|
{{if .IsProtected}}
|
||||||
{{if .IsProtected}}
|
{{svg "octicon-shield-lock"}}
|
||||||
{{svg "octicon-shield-lock"}}
|
|
||||||
{{end}}
|
|
||||||
<a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .Name}}">{{.Name}}</a>
|
|
||||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.locale.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.locale}}</p>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
<a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a>
|
||||||
<td class="three wide ui">
|
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime $.locale}}{{if .DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "Context" $.Context "user" .DBBranch.Pusher}} {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
|
||||||
{{if and (not .IsDeleted) $.DefaultBranchBranch}}
|
{{end}}
|
||||||
<div class="commit-divergence">
|
</td>
|
||||||
<div class="bar-group">
|
<td class="two wide ui">
|
||||||
<div class="count count-behind">{{.CommitsBehind}}</div>
|
{{if and (not .DBBranch.IsDeleted) $.DefaultBranchBranch}}
|
||||||
{{/* old code bears 0/0.0 = NaN output, so it might output invalid "width: NaNpx", it just works and doesn't caues any problem. */}}
|
<div class="commit-divergence">
|
||||||
<div class="bar bar-behind" style="width: {{Eval 100 "*" .CommitsBehind "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
|
<div class="bar-group">
|
||||||
</div>
|
<div class="count count-behind">{{.CommitsBehind}}</div>
|
||||||
<div class="bar-group">
|
{{/* old code bears 0/0.0 = NaN output, so it might output invalid "width: NaNpx", it just works and doesn't caues any problem. */}}
|
||||||
<div class="count count-ahead">{{.CommitsAhead}}</div>
|
<div class="bar bar-behind" style="width: {{Eval 100 "*" .CommitsBehind "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
|
||||||
<div class="bar bar-ahead" style="width: {{Eval 100 "*" .CommitsAhead "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
|
</div>
|
||||||
|
<div class="bar-group">
|
||||||
|
<div class="count count-ahead">{{.CommitsAhead}}</div>
|
||||||
|
<div class="bar bar-ahead" style="width: {{Eval 100 "*" .CommitsAhead "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="two wide right aligned">
|
||||||
|
{{if not .LatestPullRequest}}
|
||||||
|
{{if .IsIncluded}}
|
||||||
|
<span class="ui orange large label" data-tooltip-content="{{$.locale.Tr "repo.branch.included_desc"}}">
|
||||||
|
{{svg "octicon-git-pull-request"}} {{$.locale.Tr "repo.branch.included"}}
|
||||||
|
</span>
|
||||||
|
{{else if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
|
||||||
|
<a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .DBBranch.Name}}">
|
||||||
|
<button id="new-pull-request" class="ui compact basic button gt-mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button>
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
|
||||||
|
{{if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
|
||||||
|
<a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranchBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | PathEscapeSegments}}">
|
||||||
|
<button id="new-pull-request" class="ui compact basic button gt-mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button>
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
<a href="{{.LatestPullRequest.Issue.Link}}" class="gt-vm ref-issue">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
|
||||||
|
{{if .LatestPullRequest.HasMerged}}
|
||||||
|
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label purple large label gt-vm">{{svg "octicon-git-merge" 16 "gt-mr-2"}}{{$.locale.Tr "repo.pulls.merged"}}</a>
|
||||||
|
{{else if .LatestPullRequest.Issue.IsClosed}}
|
||||||
|
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label red large label gt-vm">{{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{$.locale.Tr "repo.issues.closed_title"}}</a>
|
||||||
|
{{else}}
|
||||||
|
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label green large label gt-vm">{{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{$.locale.Tr "repo.issues.open_title"}}</a>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="three wide right aligned overflow-visible">
|
||||||
|
{{if and $.IsWriter (not $.Repository.IsArchived) (not .DBBranch.IsDeleted)}}
|
||||||
|
<button class="btn interact-bg gt-p-3 show-modal show-create-branch-modal"
|
||||||
|
data-branch-from="{{.DBBranch.Name}}"
|
||||||
|
data-branch-from-urlcomponent="{{PathEscapeSegments .DBBranch.Name}}"
|
||||||
|
data-tooltip-content="{{$.locale.Tr "repo.branch.new_branch_from" .DBBranch.Name}}"
|
||||||
|
data-modal="#create-branch-modal" data-name="{{.DBBranch.Name}}"
|
||||||
|
>
|
||||||
|
{{svg "octicon-git-branch"}}
|
||||||
|
</button>
|
||||||
|
{{end}}
|
||||||
|
{{if $.EnableFeed}}
|
||||||
|
<a role="button" class="btn interact-bg gt-p-3" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}">{{svg "octicon-rss"}}</a>
|
||||||
|
{{end}}
|
||||||
|
{{if and (not .DBBranch.IsDeleted) (not $.DisableDownloadSourceArchives)}}
|
||||||
|
<div class="ui dropdown btn interact-bg gt-p-3" data-tooltip-content="{{$.locale.Tr "repo.branch.download" (.DBBranch.Name)}}">
|
||||||
|
{{svg "octicon-download"}}
|
||||||
|
<div class="menu">
|
||||||
|
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments .DBBranch.Name}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} ZIP</a>
|
||||||
|
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments .DBBranch.Name}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} TAR.GZ</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
{{if and $.IsWriter (not $.Repository.IsArchived) (not .DBBranch.IsDeleted) (not $.IsMirror)}}
|
||||||
<td class="three wide right aligned">
|
<button class="btn interact-bg gt-p-3 show-modal show-rename-branch-modal"
|
||||||
{{if not .LatestPullRequest}}
|
data-is-default-branch="false"
|
||||||
{{if .IsIncluded}}
|
data-old-branch-name="{{.DBBranch.Name}}"
|
||||||
<span class="ui orange large label" data-tooltip-content="{{$.locale.Tr "repo.branch.included_desc"}}">
|
data-modal="#rename-branch-modal"
|
||||||
{{svg "octicon-git-pull-request"}} {{$.locale.Tr "repo.branch.included"}}
|
data-tooltip-content="{{$.locale.Tr "repo.branch.rename" (.DBBranch.Name)}}"
|
||||||
|
>
|
||||||
|
{{svg "octicon-pencil"}}
|
||||||
|
</button>
|
||||||
|
{{end}}
|
||||||
|
{{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}}
|
||||||
|
{{if .DBBranch.IsDeleted}}
|
||||||
|
<button class="btn interact-bg gt-p-3 link-action restore-branch-button" data-url="{{$.Link}}/restore?branch_id={{.DBBranch.ID}}&name={{.DBBranch.Name}}&page={{$.Page.Paginater.Current}}" data-tooltip-content="{{$.locale.Tr "repo.branch.restore" (.DBBranch.Name)}}">
|
||||||
|
<span class="text blue">
|
||||||
|
{{svg "octicon-reply"}}
|
||||||
</span>
|
</span>
|
||||||
{{else if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
|
</button>
|
||||||
<a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .Name}}">
|
|
||||||
<button id="new-pull-request" class="ui compact basic button gt-mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button>
|
|
||||||
</a>
|
|
||||||
{{end}}
|
|
||||||
{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
|
|
||||||
{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
|
|
||||||
<a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | PathEscapeSegments}}">
|
|
||||||
<button id="new-pull-request" class="ui compact basic button gt-mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button>
|
|
||||||
</a>
|
|
||||||
{{end}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="{{.LatestPullRequest.Issue.Link}}" class="gt-vm ref-issue">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
|
<button class="btn interact-bg gt-p-3 delete-button delete-branch-button" data-url="{{$.Link}}/delete?name={{.DBBranch.Name}}&page={{$.Page.Paginater.Current}}" data-tooltip-content="{{$.locale.Tr "repo.branch.delete" (.DBBranch.Name)}}" data-name="{{.DBBranch.Name}}">
|
||||||
{{if .LatestPullRequest.HasMerged}}
|
{{svg "octicon-trash"}}
|
||||||
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label purple large label gt-vm">{{svg "octicon-git-merge" 16 "gt-mr-2"}}{{$.locale.Tr "repo.pulls.merged"}}</a>
|
|
||||||
{{else if .LatestPullRequest.Issue.IsClosed}}
|
|
||||||
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label red large label gt-vm">{{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{$.locale.Tr "repo.issues.closed_title"}}</a>
|
|
||||||
{{else}}
|
|
||||||
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label green large label gt-vm">{{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{$.locale.Tr "repo.issues.open_title"}}</a>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
</td>
|
|
||||||
<td class="three wide right aligned overflow-visible">
|
|
||||||
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
|
|
||||||
<button class="btn interact-bg gt-p-3 show-modal show-create-branch-modal"
|
|
||||||
data-branch-from="{{.Name}}"
|
|
||||||
data-branch-from-urlcomponent="{{PathEscapeSegments .Name}}"
|
|
||||||
data-tooltip-content="{{$.locale.Tr "repo.branch.new_branch_from" .Name}}"
|
|
||||||
data-modal="#create-branch-modal" data-name="{{.Name}}"
|
|
||||||
>
|
|
||||||
{{svg "octicon-git-branch"}}
|
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if $.EnableFeed}}
|
{{end}}
|
||||||
<a role="button" class="btn interact-bg gt-p-3" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .Name}}">{{svg "octicon-rss"}}</a>
|
</td>
|
||||||
{{end}}
|
</tr>
|
||||||
{{if and (not .IsDeleted) (not $.DisableDownloadSourceArchives)}}
|
|
||||||
<div class="ui dropdown btn interact-bg gt-p-3" data-tooltip-content="{{$.locale.Tr "repo.branch.download" (.Name)}}">
|
|
||||||
{{svg "octicon-download"}}
|
|
||||||
<div class="menu">
|
|
||||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments .Name}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} ZIP</a>
|
|
||||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments .Name}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} TAR.GZ</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted) (not $.IsMirror)}}
|
|
||||||
<button class="btn interact-bg gt-p-3 show-modal show-rename-branch-modal"
|
|
||||||
data-is-default-branch="false"
|
|
||||||
data-old-branch-name="{{.Name}}"
|
|
||||||
data-modal="#rename-branch-modal"
|
|
||||||
data-tooltip-content="{{$.locale.Tr "repo.branch.rename" (.Name)}}"
|
|
||||||
>
|
|
||||||
{{svg "octicon-pencil"}}
|
|
||||||
</button>
|
|
||||||
{{end}}
|
|
||||||
{{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}}
|
|
||||||
{{if .IsDeleted}}
|
|
||||||
<button class="btn interact-bg gt-p-3 link-action restore-branch-button" data-url="{{$.Link}}/restore?branch_id={{.DeletedBranch.ID}}&name={{.DeletedBranch.Name}}&page={{$.Page.Paginater.Current}}" data-tooltip-content="{{$.locale.Tr "repo.branch.restore" (.Name)}}">
|
|
||||||
<span class="text blue">
|
|
||||||
{{svg "octicon-reply"}}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{{else}}
|
|
||||||
<button class="btn interact-bg gt-p-3 delete-button delete-branch-button" data-url="{{$.Link}}/delete?name={{.Name}}&page={{$.Page.Paginater.Current}}" data-tooltip-content="{{$.locale.Tr "repo.branch.delete" (.Name)}}" data-name="{{.Name}}">
|
|
||||||
{{svg "octicon-trash"}}
|
|
||||||
</button>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -124,7 +124,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>.gitignore</label>
|
<label>.gitignore</label>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
{{template "repo/upload" .}}
|
{{template "repo/upload" .}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
|
{{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
|
||||||
{{if $showSelfTooltip}}
|
{{if $showSelfTooltip}}
|
||||||
<span class="gt-dib" data-tooltip-content="{{$.locale.Tr "repo.diff.review.self_approve"}}">
|
<span class="gt-dib" data-tooltip-content="{{$.locale.Tr "repo.diff.review.self_approve"}}">
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if not .Repository.IsArchived}}
|
{{if not .Repository.IsArchived}}
|
||||||
<div class="ui divider gt-my-0"></div>
|
<div class="divider gt-my-0"></div>
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<h3>{{.locale.Tr "repo.create_new_repo_command"}}</h3>
|
<h3>{{.locale.Tr "repo.create_new_repo_command"}}</h3>
|
||||||
@ -58,7 +58,7 @@ git remote add origin <span class="js-clone-url">{{$.CloneButtonOriginLink.HTTPS
|
|||||||
git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<h3>{{.locale.Tr "repo.push_exist_repo"}}</h3>
|
<h3>{{.locale.Tr "repo.push_exist_repo"}}</h3>
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if $.CanSignedUserFork}}
|
{{if $.CanSignedUserFork}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<a href="{{AppSubUrl}}/repo/fork/{{.ID}}">
|
<a href="{{AppSubUrl}}/repo/fork/{{.ID}}">
|
||||||
{{$.locale.Tr "repo.fork_to_different_account"}}
|
{{$.locale.Tr "repo.fork_to_different_account"}}
|
||||||
</a>
|
</a>
|
||||||
|
@ -50,5 +50,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
{{template "repo/issue/navbar" .}}
|
{{template "repo/issue/navbar" .}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{range .IssueTemplates}}
|
{{range .IssueTemplates}}
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
<div class="ui two column grid">
|
<div class="ui two column grid">
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
{{if and (not $.PageIsOrgSettingsLabels) (or $.CanWriteIssues $.CanWritePulls) (eq .NumLabels 0) (not $.Repository.IsArchived)}}
|
{{if and (not $.PageIsOrgSettingsLabels) (or $.CanWriteIssues $.CanWritePulls) (eq .NumLabels 0) (not $.Repository.IsArchived)}}
|
||||||
{{template "repo/issue/labels/label_load_template" .}}
|
{{template "repo/issue/labels/label_load_template" .}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{else if and ($.PageIsOrgSettingsLabels) (eq .NumLabels 0)}}
|
{{else if and ($.PageIsOrgSettingsLabels) (eq .NumLabels 0)}}
|
||||||
{{template "repo/issue/labels/label_load_template" .}}
|
{{template "repo/issue/labels/label_load_template" .}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<ul class="issue-label-list">
|
<ul class="issue-label-list">
|
||||||
|
@ -18,18 +18,18 @@
|
|||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
{{$exclusiveScope := .ExclusiveScope}}
|
{{$exclusiveScope := .ExclusiveScope}}
|
||||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{$previousExclusiveScope = $exclusiveScope}}
|
{{$previousExclusiveScope = $exclusiveScope}}
|
||||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel $.Context .}}
|
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel $.Context .}}
|
||||||
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
|
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{$previousExclusiveScope := "_no_scope"}}
|
{{$previousExclusiveScope := "_no_scope"}}
|
||||||
{{range .OrgLabels}}
|
{{range .OrgLabels}}
|
||||||
{{$exclusiveScope := .ExclusiveScope}}
|
{{$exclusiveScope := .ExclusiveScope}}
|
||||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{$previousExclusiveScope = $exclusiveScope}}
|
{{$previousExclusiveScope = $exclusiveScope}}
|
||||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel $.Context .}}
|
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel $.Context .}}
|
||||||
|
@ -102,14 +102,14 @@
|
|||||||
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_label"}}">
|
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_label"}}">
|
||||||
</div>
|
</div>
|
||||||
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
|
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
|
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
|
||||||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
|
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
|
||||||
{{$previousExclusiveScope := "_no_scope"}}
|
{{$previousExclusiveScope := "_no_scope"}}
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
{{$exclusiveScope := .ExclusiveScope}}
|
{{$exclusiveScope := .ExclusiveScope}}
|
||||||
{{if and (ne $previousExclusiveScope $exclusiveScope)}}
|
{{if and (ne $previousExclusiveScope $exclusiveScope)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{$previousExclusiveScope = $exclusiveScope}}
|
{{$previousExclusiveScope = $exclusiveScope}}
|
||||||
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context .}}</a>
|
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context .}}</a>
|
||||||
@ -306,7 +306,7 @@
|
|||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
{{$exclusiveScope := .ExclusiveScope}}
|
{{$exclusiveScope := .ExclusiveScope}}
|
||||||
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{$previousExclusiveScope = $exclusiveScope}}
|
{{$previousExclusiveScope = $exclusiveScope}}
|
||||||
<div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
<div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<div class="gt-mr-3">{{.locale.Tr "repo.milestones.completeness" .Milestone.Completeness | Safe}}</div>
|
<div class="gt-mr-3">{{.locale.Tr "repo.milestones.completeness" .Milestone.Completeness | Safe}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div id="issue-filters" class="issue-list-toolbar">
|
<div id="issue-filters" class="issue-list-toolbar">
|
||||||
<div class="issue-list-toolbar-left">
|
<div class="issue-list-toolbar-left">
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<h2 class="ui dividing header">
|
<h2 class="ui dividing header">
|
||||||
{{if .PageIsEditMilestone}}
|
{{if .PageIsEditMilestone}}
|
||||||
{{.locale.Tr "repo.milestones.edit"}}
|
{{.locale.Tr "repo.milestones.edit"}}
|
||||||
@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui right">
|
<div class="ui right">
|
||||||
{{if .PageIsEditMilestone}}
|
{{if .PageIsEditMilestone}}
|
||||||
<a class="ui primary basic button" href="{{.RepoLink}}/milestones">
|
<a class="ui primary basic button" href="{{.RepoLink}}/milestones">
|
||||||
|
@ -114,7 +114,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<a class="link-action flex-text-inline" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-x" 14}}{{$.locale.Tr "repo.milestones.close"}}</a>
|
<a class="link-action flex-text-inline" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-x" 14}}{{$.locale.Tr "repo.milestones.close"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<a class="delete-button flex-text-inline" href="#" data-url="{{$.RepoLink}}/milestones/delete">{{svg "octicon-trash" 14}}{{$.locale.Tr "repo.issues.label_delete"}}</a>
|
<a class="delete-button flex-text-inline" href="#" data-url="{{$.RepoLink}}/milestones/delete" data-id="{{.ID}}">{{svg "octicon-trash" 14}}{{$.locale.Tr "repo.issues.label_delete"}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
{{template "repo/issue/labels/labels_selector_field" .}}
|
{{template "repo/issue/labels/labels_selector_field" .}}
|
||||||
{{template "repo/issue/labels/labels_sidebar" dict "root" $}}
|
{{template "repo/issue/labels/labels_sidebar" dict "root" $}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<input id="milestone_id" name="milestone_id" type="hidden" value="{{.milestone_id}}">
|
<input id="milestone_id" name="milestone_id" type="hidden" value="{{.milestone_id}}">
|
||||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-milestone dropdown">
|
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-milestone dropdown">
|
||||||
@ -82,7 +82,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .IsProjectsEnabled}}
|
{{if .IsProjectsEnabled}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<input id="project_id" name="project_id" type="hidden" value="{{.project_id}}">
|
<input id="project_id" name="project_id" type="hidden" value="{{.project_id}}">
|
||||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-project dropdown">
|
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-project dropdown">
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<input id="assignee_ids" name="assignee_ids" type="hidden" value="{{.assignee_ids}}">
|
<input id="assignee_ids" name="assignee_ids" type="hidden" value="{{.assignee_ids}}">
|
||||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-assignees dropdown">
|
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-assignees dropdown">
|
||||||
<span class="text">
|
<span class="text">
|
||||||
@ -177,7 +177,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if and .PageIsComparePull (not (eq .HeadRepo.FullName .BaseCompareRepo.FullName)) .CanWriteToHeadRepo}}
|
{{if and .PageIsComparePull (not (eq .HeadRepo.FullName .BaseCompareRepo.FullName)) .CanWriteToHeadRepo}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label data-tooltip-content="{{.locale.Tr "repo.pulls.allow_edits_from_maintainers_desc"}}"><strong>{{.locale.Tr "repo.pulls.allow_edits_from_maintainers"}}</strong></label>
|
<label data-tooltip-content="{{.locale.Tr "repo.pulls.allow_edits_from_maintainers_desc"}}"><strong>{{.locale.Tr "repo.pulls.allow_edits_from_maintainers"}}</strong></label>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="dropzone-attachments">
|
<div class="dropzone-attachments">
|
||||||
{{if .Attachments}}
|
{{if .Attachments}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{$hasThumbnails := false}}
|
{{$hasThumbnails := false}}
|
||||||
{{- range .Attachments -}}
|
{{- range .Attachments -}}
|
||||||
@ -25,7 +25,7 @@
|
|||||||
{{end -}}
|
{{end -}}
|
||||||
|
|
||||||
{{if $hasThumbnails}}
|
{{if $hasThumbnails}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui small thumbnails">
|
<div class="ui small thumbnails">
|
||||||
{{- range .Attachments -}}
|
{{- range .Attachments -}}
|
||||||
{{if FilenameIsImage .Name}}
|
{{if FilenameIsImage .Name}}
|
||||||
|
@ -179,7 +179,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{template "repo/issue/view_content/update_branch_by_merge" $}}
|
{{template "repo/issue/view_content/update_branch_by_merge" $}}
|
||||||
{{if .Issue.PullRequest.IsEmpty}}
|
{{if .Issue.PullRequest.IsEmpty}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
{{svg "octicon-alert"}}
|
{{svg "octicon-alert"}}
|
||||||
@ -196,7 +196,7 @@
|
|||||||
{{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix $.locale}}
|
{{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix $.locale}}
|
||||||
{{$hasPendingPullRequestMergeTip = $.locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}}
|
{{$hasPendingPullRequestMergeTip = $.locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
const issueUrl = window.location.origin + {{$.Issue.Link}};
|
const issueUrl = window.location.origin + {{$.Issue.Link}};
|
||||||
const defaultMergeTitle = {{.DefaultMergeMessage}};
|
const defaultMergeTitle = {{.DefaultMergeMessage}};
|
||||||
@ -276,7 +276,7 @@
|
|||||||
<div id="pull-request-merge-form"></div>
|
<div id="pull-request-merge-form"></div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{/* no merge style was set in repo setting: not or ($prUnit.PullRequestsConfig.AllowMerge ...) */}}
|
{{/* no merge style was set in repo setting: not or ($prUnit.PullRequestsConfig.AllowMerge ...) */}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="item text red">
|
<div class="item text red">
|
||||||
{{svg "octicon-x"}}
|
{{svg "octicon-x"}}
|
||||||
{{$.locale.Tr "repo.pulls.no_merge_desc"}}
|
{{$.locale.Tr "repo.pulls.no_merge_desc"}}
|
||||||
@ -288,7 +288,7 @@
|
|||||||
{{end}} {{/* end if the repo was set to use any merge style */}}
|
{{end}} {{/* end if the repo was set to use any merge style */}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{/* user is not allowed to merge */}}
|
{{/* user is not allowed to merge */}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
{{svg "octicon-info"}}
|
{{svg "octicon-info"}}
|
||||||
{{$.locale.Tr "repo.pulls.no_merge_access"}}
|
{{$.locale.Tr "repo.pulls.no_merge_access"}}
|
||||||
@ -358,7 +358,7 @@
|
|||||||
* Then the Manually Merged form will be shown to repo admin users
|
* Then the Manually Merged form will be shown to repo admin users
|
||||||
*/}}
|
*/}}
|
||||||
{{if and $.StillCanManualMerge (not $showGeneralMergeForm)}}
|
{{if and $.StillCanManualMerge (not $showGeneralMergeForm)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<form action="{{.Link}}/merge" method="post">
|
<form action="{{.Link}}/merge" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="instruct-toggle"> {{$.locale.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div>
|
<div class="instruct-toggle"> {{$.locale.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div>
|
||||||
<div class="instruct-content gt-mt-3 gt-hidden">
|
<div class="instruct-content gt-mt-3 gt-hidden">
|
||||||
<div><h3 class="gt-di">{{$.locale.Tr "step1"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step1_desc"}}</div>
|
<div><h3 class="gt-di">{{$.locale.Tr "step1"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step1_desc"}}</div>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .TeamReviewers}}
|
{{if .TeamReviewers}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{range .TeamReviewers}}
|
{{range .TeamReviewers}}
|
||||||
{{if .Team}}
|
{{if .Team}}
|
||||||
<a class="{{if not .CanChange}}ui{{end}} item {{if .Checked}}checked{{end}} {{if not .CanChange}}ban-change{{end}}" href="#" data-id="{{.ItemID}}" data-id-selector="#review_request_team_{{.Team.ID}}" {{if not .CanChange}} data-tooltip-content="{{$.locale.Tr "repo.issues.remove_request_review_block"}}"{{end}}>
|
<a class="{{if not .CanChange}}ui{{end}} item {{if .Checked}}checked{{end}} {{if not .CanChange}}ban-change{{end}}" href="#" data-id="{{.ItemID}}" data-id-selector="#review_request_team_{{.Team.ID}}" {{if not .CanChange}} data-tooltip-content="{{$.locale.Tr "repo.issues.remove_request_review_block"}}"{{end}}>
|
||||||
@ -118,13 +118,13 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{template "repo/issue/labels/labels_selector_field" .}}
|
{{template "repo/issue/labels/labels_selector_field" .}}
|
||||||
{{template "repo/issue/labels/labels_sidebar" dict "root" $}}
|
{{template "repo/issue/labels/labels_sidebar" dict "root" $}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-milestone dropdown">
|
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-milestone dropdown">
|
||||||
<a class="text gt-df gt-ac muted">
|
<a class="text gt-df gt-ac muted">
|
||||||
@ -150,7 +150,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .IsProjectsEnabled}}
|
{{if .IsProjectsEnabled}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-project dropdown">
|
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-project dropdown">
|
||||||
<a class="text gt-df gt-ac muted">
|
<a class="text gt-df gt-ac muted">
|
||||||
@ -203,7 +203,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}">
|
<input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}">
|
||||||
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-assignees-modify dropdown">
|
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-assignees-modify dropdown">
|
||||||
@ -251,7 +251,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
{{if .Participants}}
|
{{if .Participants}}
|
||||||
<span class="text"><strong>{{.locale.Tr "repo.issues.num_participants" .NumParticipants}}</strong></span>
|
<span class="text"><strong>{{.locale.Tr "repo.issues.num_participants" .NumParticipants}}</strong></span>
|
||||||
@ -265,7 +265,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if and $.IssueWatch (not .Repository.IsArchived)}}
|
{{if and $.IssueWatch (not .Repository.IsArchived)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="ui watching">
|
<div class="ui watching">
|
||||||
<span class="text"><strong>{{.locale.Tr "notification.notifications"}}</strong></span>
|
<span class="text"><strong>{{.locale.Tr "notification.notifications"}}</strong></span>
|
||||||
@ -288,7 +288,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{if .Repository.IsTimetrackerEnabled $.Context}}
|
{{if .Repository.IsTimetrackerEnabled $.Context}}
|
||||||
{{if and .CanUseTimetracker (not .Repository.IsArchived)}}
|
{{if and .CanUseTimetracker (not .Repository.IsArchived)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui timetrack">
|
<div class="ui timetrack">
|
||||||
<span class="text"><strong>{{.locale.Tr "repo.issues.tracker"}}</strong></span>
|
<span class="text"><strong>{{.locale.Tr "repo.issues.tracker"}}</strong></span>
|
||||||
<div class="gt-mt-3">
|
<div class="gt-mt-3">
|
||||||
@ -328,7 +328,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if gt (len .WorkingUsers) 0}}
|
{{if gt (len .WorkingUsers) 0}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui comments">
|
<div class="ui comments">
|
||||||
<span class="text"><strong>{{.locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time) | Safe}}</strong></span>
|
<span class="text"><strong>{{.locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time) | Safe}}</strong></span>
|
||||||
<div>
|
<div>
|
||||||
@ -350,7 +350,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<span class="text"><strong>{{.locale.Tr "repo.issues.due_date"}}</strong></span>
|
<span class="text"><strong>{{.locale.Tr "repo.issues.due_date"}}</strong></span>
|
||||||
<div class="ui form" id="deadline-loader">
|
<div class="ui form" id="deadline-loader">
|
||||||
<div class="ui negative message gt-hidden" id="deadline-err-invalid-date">
|
<div class="ui negative message gt-hidden" id="deadline-err-invalid-date">
|
||||||
@ -394,7 +394,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .Repository.IsDependenciesEnabled $.Context}}
|
{{if .Repository.IsDependenciesEnabled $.Context}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="ui depending">
|
<div class="ui depending">
|
||||||
{{if (and (not .BlockedByDependencies) (not .BlockedByDependenciesNotPermitted) (not .BlockingDependencies) (not .BlockingDependenciesNotPermitted))}}
|
{{if (and (not .BlockedByDependencies) (not .BlockedByDependenciesNotPermitted) (not .BlockingDependencies) (not .BlockingDependenciesNotPermitted))}}
|
||||||
@ -543,7 +543,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui equal width compact grid">
|
<div class="ui equal width compact grid">
|
||||||
{{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
|
{{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
|
||||||
<div class="row gt-ac" data-tooltip-content="{{$issueReferenceLink}}">
|
<div class="row gt-ac" data-tooltip-content="{{$issueReferenceLink}}">
|
||||||
@ -553,10 +553,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if and .IsRepoAdmin (not .Repository.IsArchived)}}
|
{{if and .IsRepoAdmin (not .Repository.IsArchived)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
{{if or .PinEnabled .Issue.IsPinned}}
|
{{if or .PinEnabled .Issue.IsPinned}}
|
||||||
<form class="gt-mt-2" method="post" {{if $.NewPinAllowed}}action="{{.Issue.Link}}/pin"{{else}}data-tooltip-content="{{.locale.Tr "repo.issues.max_pinned"}}"{{end}}>
|
<form class="gt-mt-2 form-fetch-action single-button-form" method="post" {{if $.NewPinAllowed}}action="{{.Issue.Link}}/pin"{{else}}data-tooltip-content="{{.locale.Tr "repo.issues.max_pinned"}}"{{end}}>
|
||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
<button class="fluid ui button {{if not $.NewPinAllowed}}disabled{{end}}">
|
<button class="fluid ui button {{if not $.NewPinAllowed}}disabled{{end}}">
|
||||||
{{if not .Issue.IsPinned}}
|
{{if not .Issue.IsPinned}}
|
||||||
@ -599,7 +599,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="ui form" action="{{.Issue.Link}}{{if .Issue.IsLocked}}/unlock{{else}}/lock{{end}}"
|
<form class="ui form form-fetch-action" action="{{.Issue.Link}}{{if .Issue.IsLocked}}/unlock{{else}}/lock{{end}}"
|
||||||
method="post">
|
method="post">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
|
||||||
@ -673,7 +673,7 @@
|
|||||||
|
|
||||||
{{if and .Issue.IsPull .IsIssuePoster (not .Issue.IsClosed) .Issue.PullRequest.HeadRepo}}
|
{{if and .Issue.IsPull .IsIssuePoster (not .Issue.IsClosed) .Issue.PullRequest.HeadRepo}}
|
||||||
{{if and (not (eq .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName)) .CanWriteToHeadRepo}}
|
{{if and (not (eq .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName)) .CanWriteToHeadRepo}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<div class="ui checkbox" id="allow-edits-from-maintainers"
|
<div class="ui checkbox" id="allow-edits-from-maintainers"
|
||||||
data-url="{{.Issue.Link}}"
|
data-url="{{.Issue.Link}}"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{if and (gt $.Issue.PullRequest.CommitsBehind 0) (not $.Issue.IsClosed) (not $.Issue.PullRequest.IsChecking) (not $.IsPullFilesConflicted) (not $.IsPullRequestBroken)}}
|
{{if and (gt $.Issue.PullRequest.CommitsBehind 0) (not $.Issue.IsClosed) (not $.Issue.PullRequest.IsChecking) (not $.IsPullFilesConflicted) (not $.IsPullRequestBroken)}}
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="item item-section">
|
<div class="item item-section">
|
||||||
<div class="item-section-left flex-text-inline">
|
<div class="item-section-left flex-text-inline">
|
||||||
{{svg "octicon-alert"}}
|
{{svg "octicon-alert"}}
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="inline required field {{if .Err_Owner}}error{{end}}">
|
<div class="inline required field {{if .Err_Owner}}error{{end}}">
|
||||||
<label>{{.locale.Tr "repo.owner"}}</label>
|
<label>{{.locale.Tr "repo.owner"}}</label>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
{{template "repo/migrate/options" .}}
|
{{template "repo/migrate/options" .}}
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="inline required field {{if .Err_Owner}}error{{end}}">
|
<div class="inline required field {{if .Err_Owner}}error{{end}}">
|
||||||
<label>{{.locale.Tr "repo.owner"}}</label>
|
<label>{{.locale.Tr "repo.owner"}}</label>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user