Compare commits

...

38 Commits

Author SHA1 Message Date
Matti R
f5845e6497 Changelog for 1.15.4 2021-10-08 14:01:42 -04:00
Norwin
c927ebd119 API: don't allow merged PRs to be reopened (#17271) 2021-10-08 13:54:26 -04:00
Norwin
245596e130 don't try to interpret treepath as hash (#17272)
...when path contains no hash-path-separator ('/')

This is a workaround to #17179.

Entering this case when `path` does not contain a '/' does not really
make sense, as that means the tree path is empty, but this case is only
entered for routes that expect a non-empty tree path.

Treepaths like <40-char-dirname>/<filename> will still fail,
but hopefully don't occur that often. A more complete fix that avoids
this case too is outlined in #17185, but too big of a change to backport
2021-10-08 13:53:54 -04:00
Jimmy Praet
1c3ae6d05e Fix incorrect repository count on organization tab of dashboard (#17266)
Fixes #17249
2021-10-08 17:33:16 +08:00
Jimmy Praet
a1e57ebe6b Fix unwanted team review request deletion (#17257) (#17264)
Add missing issue_id = ? to where clause
Fixes #17251
2021-10-07 23:58:13 +02:00
6543
73ae93b007 CI: migrate from 'plugins/s3:1' to 'woodpeckerci/plugin-s3:latest' (#17234) (#17260)
- this fixes the CI release upload issues, as the docker image for this is freshly built (unlike the mostly unmaintained "official" drone plugins), thus containing current CA certs needed for letsencrypt since 2021-09-31.
- woodpecker is a drone-ci fork maintained partially by @6543. it's API compatible with current drone plugins afaik
2021-10-07 22:02:07 +02:00
6543
dc030f64a7 Remove dead badge on README.md (#17261) 2021-10-07 15:40:11 -04:00
Jimmy Praet
6e0a08d753 Fix broken Activities link in team dashboard (#17255) (#17258)
Remove '/' suffix from organization dashboard link

Fixes #17250
2021-10-07 20:58:59 +02:00
pricly-yellow
7b1153e943 API pull's head/base have correct permission(#17214) (#17245)
* for all pull requests API return permissions of caller
* for all webhook return empty permissions

Signed-off-by: Danila Kryukov <pricly_yellow@dismail.de>

* Fix incorrect error handler

Co-authored-by: delvh <dev.lh@web.de>

* Fix wrong assumption in tests

* Change paramenter name to doer to indicate source

Co-authored-by: 6543 <6543@obermui.de>

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: 6543 <6543@obermui.de>
2021-10-07 12:39:23 +03:00
pricly-yellow
6995be66e7 Fix stange behavior of DownloadPullDiffOrPatch in incorect index (#17223) (#17227)
Fix GetPullRequestByIndex by validate index > 1

Signed-off-by: Danila Kryukov <pricly_yellow@dismail.de>
Co-authored-by: a1012112796 <1012112796@qq.com>
2021-10-05 20:16:22 +02:00
6543
28971c7c15 Check user instead of organization when creating a repo from a template via API (#16346) (#17195)
* Check user instead of organization

* Enforce that only admins can copy a repo to another user

Co-authored-by: Ion Jaureguialzo Sarasola <ion@jaureguialzo.com>
2021-10-01 10:16:28 +02:00
Lunny Xiao
eb5e6f09eb upgrade xorm to v1.2.5 (#17177) (#17188) 2021-09-30 07:03:42 +01:00
Alexey 〒erentyev
bf6264c1db fix sprintf verbs in locales (#17187)
Signed-off-by: Alexey Terentyev <axifnx@gmail.com>

Co-authored-by: 6543 <6543@obermui.de>
2021-09-30 12:03:21 +08:00
zeripath
5b6b7e79cf Fix missing repo link in issue/pull assigned emails (#17183) (#17184)
Backport #17183

There was a mistake in the template file: `templates/mail/issue/assigned.tmpl`
where the repourl was generated from a non-existent release instead of the issue.

This PR changes this to use the issue but also ensure that the issue repo is loaded.

It also slightly improves the English and the Russian locale string.

Fix #17160

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: 6543 <6543@obermui.de>
2021-09-30 01:21:12 +02:00
Lunny Xiao
766272b154 Fix bug of get context user (#17169) (#17172)
Co-authored-by: 6543 <6543@obermui.de>
2021-09-28 15:42:43 +02:00
zeripath
4707d4b8a9 Nicely handle missing user in collaborations (#17049) (#17166)
Backport #17049

It is possible to have a collaboration in a repository which refers to a no-longer
existing user. This causes the repository transfer to fail with an unusual error.

This PR makes `repo.getCollaborators()` nicely handle the missing user by ghosting
the collaboration but also adds consistency check. It also adds an
Access consistency check.

Fix #17044

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2021-09-28 07:41:12 +01:00
zeripath
4b8b214108 Create doctor command to fix repo_units broken by dumps from 1.14.3-1.14.6 (#17136) (#17137)
Backport #17136

There was a serious issue with the `gitea dump` command in 1.14.3-1.14.6 which led to corruption of the `config` field of the `repo_unit` table. 

This PR adds a doctor command to attempt to fix the broken repo_units. Users affected by #16961 should run:

```
gitea doctor --fix --run fix-broken-repo-units
```

Fix #16961

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-27 18:30:11 +01:00
zeripath
ebae7e1512 Add Horizontal scrollbar to inner menu on Chrome (#17086) (#17164) 2021-09-27 12:44:22 -04:00
6543
122917f4d5 Fix wrong i18n keys (#17150) (#17153)
Co-authored-by: 6543 <6543@obermui.de>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2021-09-26 08:25:12 +08:00
Alexey 〒erentyev
9cf5739c0f correct transaction ending (#17151)
Signed-off-by: Alexey Terentyev <axifnx@gmail.com>
2021-09-25 16:45:39 +01:00
zeripath
4b6556565f Prevent panic in Org mode HighlightCodeBlock (#17140) (#17141)
Backport #17140

When rendering source in org mode there is a mistake in the highlight code that
causes a panic.

This PR fixes this.

Fix #17139

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-24 14:29:47 +01:00
zeripath
7ce938b6c7 Changelog 1.15.3 (#17091)
## [1.15.3](https://github.com/go-gitea/gitea/releases/tag/v1.15.3) - 2021-09-19

* ENHANCEMENTS
  * Add fluid to ui container class to remove margin (#16396) (#16976)
  * Add caller to cat-file batch calls (#17082) (#17089)
* BUGFIXES
  * Render full plain readme. (#17083) (#17090)
  * Upgrade xorm to v1.2.4 (#17059)
  * Fix bug of migrate comments which only fetch one page (#17055) (#17058)
  * Do not show issue context popup on external issues (#17050) (#17054)
  * Decrement Fork Num when converting from Fork (#17035) (#17046)
  * Correctly rollback in ForkRepository (#17034) (#17045)
  * Fix missing close in WalkGitLog (#17008) (#17009)
  * Add prefix to SVG id/class attributes (#16997) (#17000)
  * Fix bug of migrated repository not index (#16991) (#16996)
  * Skip AllowedUserVisibilityModes validation on update user if it is an organisation (#16988) (#16990)
  * Fix storage Iterate bug and Add storage doctor to delete garbage attachments (#16971) (#16977)
  * Fix issue with issue default mail template (#16956) (#16975)
  * Ensure that rebase conflicts are handled in updates (#16952) (#16960)
  * Prevent panic on diff generation (#16950) (#16951)

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2021-09-20 12:02:52 +08:00
zeripath
6139834e76 Add caller to cat-file batch calls (#17082) (#17089)
Some people still appear to report unclosed cat-files. This PR simply adds the caller
to the process descriptor for the CatFileBatch and CatFileBatchCheck calls.

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: techknowlogick <techknowlogick@gitea.io>

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2021-09-20 00:07:35 +08:00
zeripath
b673a24ee6 Render full plain readme. (#17083) (#17090)
Backport #17083

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2021-09-19 22:01:19 +08:00
Lunny Xiao
fd35f56e87 Fix bug of migrate comments which only fetch one page (#17055) (#17058)
* Fix bug of migrate comments which only fetch one page

* add next page to trace

Co-authored-by: zeripath <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2021-09-15 14:01:54 -04:00
Lunny Xiao
1f8df5dd89 Upgrade xorm to v1.2.4 (#17059) 2021-09-15 23:27:46 +08:00
zeripath
6a025d8b4a Do not show issue context popup on external issues (#17050) (#17054)
Backport #17050

The issues pop-up context cannot work for external issues - therefore do not show
these.

Fix #17047

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-15 17:38:20 +08:00
zeripath
270c7f36db Correctly rollback in ForkRepository (#17034) (#17045)
Backport #17034

The rollback functionality in
services/repository/repository.go:ForkRepository is incorrect and could
lead to a deadlock as it uses DeleteRepository to delete the rolled-back
repository - a function which creates its own transaction.

This PR adjusts the rollback function to only use RemoveAll as any
database changes will be automatically rolled-back. It also handles
panics and adjusts the Close within WithTx to ensure that if there is a
panic the session will always be closed.

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2021-09-15 08:42:09 +03:00
zeripath
0e448fb96d Decrement Fork Num when converting from Fork (#17035) (#17046)
Backport #17035

When converting repositories from forks to normal the root NumFork needs to be
decremented too.

Fix #17026

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-15 10:05:47 +08:00
zeripath
659b946eda Fix missing close in WalkGitLog (#17008) (#17009)
Backport #17008

When the external context is cancelled it is possible for the
GitLogReader to not itself be Closed.

This PR does three things:

1. Instead of adding a plain defer it wraps the `g.Close` in a func as
`g` may change.
2. It adds the missing explicit g.Close - although the defer fix makes
this unnecessary.
3. It passes down the external context as the base context for the
GitLogReader meaning that the cancellation of the external context will
pass down automatically.

Fix #17007

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-10 17:46:06 +08:00
KN4CK3R
56ab5ec9ea Use filename as id/class prefix. (#16997) (#17000)
Currently the svg minifier (`make svg`) rewrites all `id` and `class` attributes in svg files. Every file gets the ids `a, b, ...`. If multiple svgs with ids are used on a page these ids are conflicting and the results are broken images.

| minified ids | unique ids |
| - | - |
| ![grafik](https://user-images.githubusercontent.com/1666336/132579375-59d3996f-c4e5-43b8-8c8d-82280c90d9e3.png) | ![grafik](https://user-images.githubusercontent.com/1666336/132579413-05bf9285-4e3b-4d0d-8f95-90b212405b05.png) |

This PR adds a prefix (the filename) to every id/class.

Follow up problem: Because we embed svg images there are duplicated ids if one svg image is used multiple times on a page. As those ids refer to the same content it may be no real problem because browser handle that fine.
2021-09-09 10:47:28 +01:00
6543
3b13c5d41a Fix bug of migrated repository not index (#16991) (#16996)
Fix #16986, #16152

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2021-09-09 07:02:22 +01:00
6543
d27f061863 Skip AllowedUserVisibilityModes validation on update user if it is an organisation (#16988) (#16990)
if AllowedUserVisibilityModes allow only public & limited, and orgs can be private, a user can create a repo to that organisation whitch will result in an update of the user. On this call the user is validaten and will be rejected since private is not allowed, but its not an user its an valid org ...

Co-authored-by: Alexey 〒erentyev <axifnx@gmail.com>

Co-authored-by: Alexey 〒erentyev <axifnx@gmail.com>
2021-09-08 23:58:00 +08:00
Lunny Xiao
07489d0405 Fix storage Iterate bug and Add storage doctor to delete garbage attachments (#16971) (#16977)
* Fix storage Iterate bug and Add storage doctor to delete garbage attachments

* Close object when used
2021-09-07 19:39:05 +01:00
Prasad Katti
30708d9ffe Fix issue with issue default mail template (#16956) (#16975)
Backport #16956

The mail template rendering was failing with the error -
`...vices/mailer/mail.go:301:composeIssueCommentMessages() [E] ExecuteTemplate [issue/default/body]: template: issue/default:65:10: executing "issue/default" at <.i18n.Tr>: can't evaluate field i18n in type *models.Comment`

The issue was the template variable i18n is available in the outer scope.

Fix #16877

Co-authored-by: 6543 <6543@obermui.de>
2021-09-07 10:06:59 +08:00
silverwind
1b08dfeacf Add fluid to ui container class to remove margin (#16396) (#16976)
Co-authored-by: Stanley Hu <stanthetiger@yahoo.com>
2021-09-07 03:37:32 +02:00
zeripath
e5ded0ee19 Ensure that rebase conflicts are handled in updates (#16952) (#16960)
Backport #16952

PR #16125 did not update the error handlers to handle conflict errors relating
to rebases. This PR adds them.

Fix #16922

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-05 18:54:13 +02:00
zeripath
a384109244 Prevent panic on diff generation (#16950) (#16951)
Backport #16950

The lastLeftIdx should be reset at the same time as creating a new section otherwise
it is possible for a second addition to end up attempting to read a nil entry.

Fix #16943

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-09-05 12:17:42 +02:00
82 changed files with 1418 additions and 582 deletions

View File

@@ -527,7 +527,7 @@ steps:
- name: release-branch
pull: always
image: plugins/s3:1
image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
@@ -548,7 +548,7 @@ steps:
- push
- name: release-main
image: plugins/s3:1
image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
@@ -623,7 +623,7 @@ steps:
- name: release-tag
pull: always
image: plugins/s3:1
image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts

View File

@@ -4,6 +4,51 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.15.4](https://github.com/go-gitea/gitea/releases/tag/v1.15.4) - 2021-10-08
* BUGFIXES
* Raw file API: don't try to interpret 40char filenames as commit SHA (#17185) (#17272)
* Don't allow merged PRs to be reopened (#17192) (#17271)
* Fix incorrect repository count on organization tab of dashboard (#17256) (#17266)
* Fix unwanted team review request deletion (#17257) (#17264)
* Fix broken Activities link in team dashboard (#17255) (#17258)
* API pull's head/base have correct permission(#17214) (#17245)
* Fix stange behavior of DownloadPullDiffOrPatch in incorect index (#17223) (#17227)
* Upgrade xorm to v1.2.5 (#17177) (#17188)
* Fix missing repo link in issue/pull assigned emails (#17183) (#17184)
* Fix bug of get context user (#17169) (#17172)
* Nicely handle missing user in collaborations (#17049) (#17166)
* Add Horizontal scrollbar to inner menu on Chrome (#17086) (#17164)
* Fix wrong i18n keys (#17150) (#17153)
* Fix Archive Creation: correct transaction ending (#17151)
* Prevent panic in Org mode HighlightCodeBlock (#17140) (#17141)
* Create doctor command to fix repo_units broken by dumps from 1.14.3-1.14.6 (#17136) (#17137)
* ENHANCEMENT
* Check user instead of organization when creating a repo from a template via API (#16346) (#17195)
* TRANSLATION
* v1.15 fix Sprintf format 'verbs' in locale files (#17187)
## [1.15.3](https://github.com/go-gitea/gitea/releases/tag/v1.15.3) - 2021-09-19
* ENHANCEMENTS
* Add fluid to ui container class to remove margin (#16396) (#16976)
* Add caller to cat-file batch calls (#17082) (#17089)
* BUGFIXES
* Render full plain readme. (#17083) (#17090)
* Upgrade xorm to v1.2.4 (#17059)
* Fix bug of migrate comments which only fetch one page (#17055) (#17058)
* Do not show issue context popup on external issues (#17050) (#17054)
* Decrement Fork Num when converting from Fork (#17035) (#17046)
* Correctly rollback in ForkRepository (#17034) (#17045)
* Fix missing close in WalkGitLog (#17008) (#17009)
* Add prefix to SVG id/class attributes (#16997) (#17000)
* Fix bug of migrated repository not index (#16991) (#16996)
* Skip AllowedUserVisibilityModes validation on update user if it is an organisation (#16988) (#16990)
* Fix storage Iterate bug and Add storage doctor to delete garbage attachments (#16971) (#16977)
* Fix issue with issue default mail template (#16956) (#16975)
* Ensure that rebase conflicts are handled in updates (#16952) (#16960)
* Prevent panic on diff generation (#16950) (#16951)
## [1.15.2](https://github.com/go-gitea/gitea/releases/tag/v1.15.2) - 2021-09-03
* BUGFIXES

View File

@@ -12,9 +12,6 @@
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
<img src="https://img.shields.io/discord/322538954119184384.svg">
</a>
<a href="https://microbadger.com/images/gitea/gitea" title="Get your own image badge on microbadger.com">
<img src="https://images.microbadger.com/badges/image/gitea/gitea.svg">
</a>
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
</a>

View File

@@ -29,6 +29,7 @@ async function processFile(file, {prefix, fullName} = {}) {
plugins: extendDefaultPlugins([
'removeXMLNS',
'removeDimensions',
{name: 'prefixIds', params: {prefix: () => name}},
{
name: 'addClassesToSVGElement',
params: {classNames: ['svg', name]},

View File

@@ -124,7 +124,6 @@ func runRecreateTable(ctx *cli.Context) error {
}
func runDoctor(ctx *cli.Context) error {
// Silence the default loggers
log.DelNamedLogger("console")
log.DelNamedLogger(log.DEFAULT)

2
go.mod
View File

@@ -139,7 +139,7 @@ require (
mvdan.cc/xurls/v2 v2.2.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.9
xorm.io/xorm v1.2.2
xorm.io/xorm v1.2.5
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1

4
go.sum
View File

@@ -1765,5 +1765,5 @@ xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.0.6/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
xorm.io/xorm v1.2.2 h1:FFBOEvJ++8fYBA9cywf2sxDVmFktl1SpJzTAG1ab06Y=
xorm.io/xorm v1.2.2/go.mod h1:fTG8tSjk6O1BYxwuohZUK+S1glnRycsCF05L1qQyEU0=
xorm.io/xorm v1.2.5 h1:tqN7OhN8P9xi52qBb76I8m5maAJMz/SSbgK2RGPCPbo=
xorm.io/xorm v1.2.5/go.mod h1:fTG8tSjk6O1BYxwuohZUK+S1glnRycsCF05L1qQyEU0=

View File

@@ -225,6 +225,9 @@ func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int6
return fmt.Errorf("getCollaborations: %v", err)
}
for _, c := range collaborators {
if c.User.IsGhost() {
continue
}
updateUserAccess(accessMap, c.User, c.Collaboration.Mode)
}
return nil

View File

@@ -144,6 +144,11 @@ func GetAttachmentByUUID(uuid string) (*Attachment, error) {
return getAttachmentByUUID(x, uuid)
}
// ExistAttachmentsByUUID returns true if attachment is exist by given UUID
func ExistAttachmentsByUUID(uuid string) (bool, error) {
return x.Where("`uuid`=?", uuid).Exist(new(Attachment))
}
// GetAttachmentByReleaseIDFileName returns attachment by given releaseId and fileName.
func GetAttachmentByReleaseIDFileName(releaseID int64, fileName string) (*Attachment, error) {
return getAttachmentByReleaseIDFileName(x, releaseID, fileName)

View File

@@ -45,19 +45,16 @@ func WithContext(f func(ctx DBContext) error) error {
// WithTx represents executing database operations on a transaction
func WithTx(f func(ctx DBContext) error) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
sess.Close()
return err
}
if err := f(DBContext{sess}); err != nil {
sess.Close()
return err
}
err := sess.Commit()
sess.Close()
return err
return sess.Commit()
}
// Iterate iterates the databases and doing something

View File

@@ -71,9 +71,9 @@ var (
_ convert.Conversion = &SSPIConfig{}
)
// jsonUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
// JSONUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
func jsonUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
func JSONUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
json := jsoniter.ConfigCompatibleWithStandardLibrary
err := json.Unmarshal(bs, v)
if err != nil {
@@ -89,7 +89,7 @@ func jsonUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
rs = append(rs, temp...)
}
if ok {
if rs[0] == 0xff && rs[1] == 0xfe {
if len(rs) > 1 && rs[0] == 0xff && rs[1] == 0xfe {
rs = rs[2:]
}
err = json.Unmarshal(rs, v)
@@ -108,7 +108,7 @@ type LDAPConfig struct {
// FromDB fills up a LDAPConfig from serialized format.
func (cfg *LDAPConfig) FromDB(bs []byte) error {
err := jsonUnmarshalHandleDoubleEncode(bs, &cfg)
err := JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err != nil {
return err
}
@@ -149,7 +149,7 @@ type SMTPConfig struct {
// FromDB fills up an SMTPConfig from serialized format.
func (cfg *SMTPConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports an SMTPConfig to a serialized format.
@@ -166,7 +166,7 @@ type PAMConfig struct {
// FromDB fills up a PAMConfig from serialized format.
func (cfg *PAMConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports a PAMConfig to a serialized format.
@@ -187,7 +187,7 @@ type OAuth2Config struct {
// FromDB fills up an OAuth2Config from serialized format.
func (cfg *OAuth2Config) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports an SMTPConfig to a serialized format.
@@ -207,7 +207,7 @@ type SSPIConfig struct {
// FromDB fills up an SSPIConfig from serialized format.
func (cfg *SSPIConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, cfg)
return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports an SSPIConfig to a serialized format.

View File

@@ -455,7 +455,7 @@ func GetUserOrgsList(user *User) ([]*MinimalOrg, error) {
groupByStr := groupByCols.String()
groupByStr = groupByStr[0 : len(groupByStr)-1]
sess.Select(groupByStr+", count(repo_id) as org_count").
sess.Select(groupByStr+", count(distinct repo_id) as org_count").
Table("user").
Join("INNER", "team", "`team`.org_id = `user`.id").
Join("INNER", "team_user", "`team`.id = `team_user`.team_id").

View File

@@ -502,6 +502,9 @@ func GetLatestPullRequestByHeadInfo(repoID int64, branch string) (*PullRequest,
// GetPullRequestByIndex returns a pull request by the given index
func GetPullRequestByIndex(repoID, index int64) (*PullRequest, error) {
if index < 1 {
return nil, ErrPullRequestNotExist{}
}
pr := &PullRequest{
BaseRepoID: repoID,
Index: index,

View File

@@ -133,6 +133,10 @@ func TestGetPullRequestByIndex(t *testing.T) {
_, err = GetPullRequestByIndex(9223372036854775807, 9223372036854775807)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
_, err = GetPullRequestByIndex(1, 0)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
}
func TestGetPullRequestByID(t *testing.T) {

View File

@@ -1217,6 +1217,12 @@ func IncrementRepoForkNum(ctx DBContext, repoID int64) error {
return err
}
// DecrementRepoForkNum decrement repository fork number
func DecrementRepoForkNum(ctx DBContext, repoID int64) error {
_, err := ctx.e.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repoID)
return err
}
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err error) {
oldRepoName := repo.Name

View File

@@ -8,6 +8,7 @@ package models
import (
"fmt"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
@@ -83,16 +84,21 @@ func (repo *Repository) getCollaborators(e Engine, listOptions ListOptions) ([]*
return nil, fmt.Errorf("getCollaborations: %v", err)
}
collaborators := make([]*Collaborator, len(collaborations))
for i, c := range collaborations {
collaborators := make([]*Collaborator, 0, len(collaborations))
for _, c := range collaborations {
user, err := getUserByID(e, c.UserID)
if err != nil {
if IsErrUserNotExist(err) {
log.Warn("Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist", c.UserID, repo)
user = NewGhostUser()
} else {
return nil, err
}
collaborators[i] = &Collaborator{
}
collaborators = append(collaborators, &Collaborator{
User: user,
Collaboration: c,
}
})
}
return collaborators, nil
}

View File

@@ -269,6 +269,14 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
// Dummy object.
collaboration := &Collaboration{RepoID: repo.ID}
for _, c := range collaborators {
if c.IsGhost() {
collaboration.ID = c.Collaboration.ID
if _, err := sess.Delete(collaboration); err != nil {
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
}
collaboration.ID = 0
}
if c.ID != newOwner.ID {
isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID)
if err != nil {
@@ -281,6 +289,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
if _, err := sess.Delete(collaboration); err != nil {
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
}
collaboration.UserID = 0
}
// Remove old team-repository relations.

View File

@@ -28,7 +28,7 @@ type UnitConfig struct{}
// FromDB fills up a UnitConfig from serialized format.
func (cfg *UnitConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a UnitConfig to a serialized format.
@@ -44,7 +44,7 @@ type ExternalWikiConfig struct {
// FromDB fills up a ExternalWikiConfig from serialized format.
func (cfg *ExternalWikiConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a ExternalWikiConfig to a serialized format.
@@ -62,7 +62,7 @@ type ExternalTrackerConfig struct {
// FromDB fills up a ExternalTrackerConfig from serialized format.
func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a ExternalTrackerConfig to a serialized format.
@@ -80,7 +80,7 @@ type IssuesConfig struct {
// FromDB fills up a IssuesConfig from serialized format.
func (cfg *IssuesConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a IssuesConfig to a serialized format.
@@ -104,7 +104,7 @@ type PullRequestsConfig struct {
// FromDB fills up a PullRequestsConfig from serialized format.
func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a PullRequestsConfig to a serialized format.
@@ -219,3 +219,9 @@ func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
return units, nil
}
// UpdateRepoUnit updates the provided repo unit
func UpdateRepoUnit(unit *RepoUnit) error {
_, err := x.ID(unit.ID).Update(unit)
return err
}

View File

@@ -434,7 +434,7 @@ func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content, comm
// try to remove team review request if need
if issue.Repo.Owner.IsOrganization() && (reviewType == ReviewTypeApprove || reviewType == ReviewTypeReject) {
teamReviewRequests := make([]*Review, 0, 10)
if err := sess.SQL("SELECT * FROM review WHERE reviewer_team_id > 0 AND type = ?", ReviewTypeRequest).Find(&teamReviewRequests); err != nil {
if err := sess.SQL("SELECT * FROM review WHERE issue_id = ? AND reviewer_team_id > 0 AND type = ?", issue.ID, ReviewTypeRequest).Find(&teamReviewRequests); err != nil {
return nil, nil, err
}

View File

@@ -296,7 +296,7 @@ func (u *User) CanImportLocal() bool {
// DashboardLink returns the user dashboard page link.
func (u *User) DashboardLink() string {
if u.IsOrganization() {
return u.OrganisationLink() + "/dashboard/"
return u.OrganisationLink() + "/dashboard"
}
return setting.AppSubURL + "/"
}
@@ -1062,9 +1062,9 @@ func checkDupEmail(e Engine, u *User) error {
return nil
}
// validateUser check if user is valide to insert / update into database
// validateUser check if user is valid to insert / update into database
func validateUser(u *User) error {
if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) {
if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) && !u.IsOrganization() {
return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String())
}

View File

@@ -587,6 +587,17 @@ func GetContext(req *http.Request) *Context {
return req.Context().Value(contextKey).(*Context)
}
// GetContextUser returns context user
func GetContextUser(req *http.Request) *models.User {
if apiContext, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
return apiContext.User
}
if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
return ctx.User
}
return nil
}
// SignedUserName returns signed user's name via context
func SignedUserName(req *http.Request) string {
if middleware.IsInternalPath(req) {

View File

@@ -695,7 +695,7 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
}
// For legacy and API support only full commit sha
parts := strings.Split(path, "/")
if len(parts) > 0 && len(parts[0]) == 40 {
if len(parts) > 1 && len(parts[0]) == 40 {
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
return parts[0]
}

View File

@@ -17,7 +17,7 @@ import (
// ToAPIPullRequest assumes following fields have been assigned with valid values:
// Required - Issue
// Optional - Merger
func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
func ToAPIPullRequest(pr *models.PullRequest, doer *models.User) *api.PullRequest {
var (
baseBranch *git.Branch
headBranch *git.Branch
@@ -41,6 +41,12 @@ func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
return nil
}
perm, err := models.GetUserRepoPermission(pr.BaseRepo, doer)
if err != nil {
log.Error("GetUserRepoPermission[%d]: %v", pr.BaseRepoID, err)
perm.AccessMode = models.AccessModeNone
}
apiPullRequest := &api.PullRequest{
ID: pr.ID,
URL: pr.Issue.HTMLURL(),
@@ -68,7 +74,7 @@ func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
Name: pr.BaseBranch,
Ref: pr.BaseBranch,
RepoID: pr.BaseRepoID,
Repository: ToRepo(pr.BaseRepo, models.AccessModeNone),
Repository: ToRepo(pr.BaseRepo, perm.AccessMode),
},
Head: &api.PRBranchInfo{
Name: pr.HeadBranch,
@@ -96,8 +102,14 @@ func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
}
if pr.HeadRepo != nil {
perm, err := models.GetUserRepoPermission(pr.HeadRepo, doer)
if err != nil {
log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)
perm.AccessMode = models.AccessModeNone
}
apiPullRequest.Head.RepoID = pr.HeadRepo.ID
apiPullRequest.Head.Repository = ToRepo(pr.HeadRepo, models.AccessModeNone)
apiPullRequest.Head.Repository = ToRepo(pr.HeadRepo, perm.AccessMode)
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
if err != nil {

View File

@@ -20,14 +20,14 @@ func TestPullRequest_APIFormat(t *testing.T) {
pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
assert.NoError(t, pr.LoadAttributes())
assert.NoError(t, pr.LoadIssue())
apiPullRequest := ToAPIPullRequest(pr)
apiPullRequest := ToAPIPullRequest(pr, nil)
assert.NotNil(t, apiPullRequest)
assert.EqualValues(t, &structs.PRBranchInfo{
Name: "branch1",
Ref: "refs/pull/2/head",
Sha: "4a357436d925b5c974181ff12a994538ddc5a269",
RepoID: 1,
Repository: ToRepo(headRepo, models.AccessModeNone),
Repository: ToRepo(headRepo, models.AccessModeRead),
}, apiPullRequest.Head)
//withOut HeadRepo
@@ -37,7 +37,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
// simulate fork deletion
pr.HeadRepo = nil
pr.HeadRepoID = 100000
apiPullRequest = ToAPIPullRequest(pr)
apiPullRequest = ToAPIPullRequest(pr, nil)
assert.NotNil(t, apiPullRequest)
assert.Nil(t, apiPullRequest.Head.Repository)
assert.EqualValues(t, -1, apiPullRequest.Head.RepoID)

View File

@@ -13,6 +13,64 @@ import (
"code.gitea.io/gitea/modules/setting"
)
type consistencyCheck struct {
Name string
Counter func() (int64, error)
Fixer func() (int64, error)
FixedMessage string
}
func (c *consistencyCheck) Run(logger log.Logger, autofix bool) error {
count, err := c.Counter()
if err != nil {
logger.Critical("Error: %v whilst counting %s", err, c.Name)
return err
}
if count > 0 {
if autofix {
var fixed int64
if fixed, err = c.Fixer(); err != nil {
logger.Critical("Error: %v whilst fixing %s", err, c.Name)
return err
}
prompt := "Deleted"
if c.FixedMessage != "" {
prompt = c.FixedMessage
}
if fixed < 0 {
logger.Info(prompt+" %d %s", count, c.Name)
} else {
logger.Info(prompt+" %d/%d %s", fixed, count, c.Name)
}
} else {
logger.Warn("Found %d %s", count, c.Name)
}
}
return nil
}
func asFixer(fn func() error) func() (int64, error) {
return func() (int64, error) {
err := fn()
return -1, err
}
}
func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCheck {
return consistencyCheck{
Name: name,
Counter: func() (int64, error) {
return models.CountOrphanedObjects(subject, refobject, joincond)
},
Fixer: func() (int64, error) {
err := models.DeleteOrphanedObjects(subject, refobject, joincond)
return -1, err
},
}
}
func checkDBConsistency(logger log.Logger, autofix bool) error {
// make sure DB version is uptodate
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
@@ -20,246 +78,103 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
return err
}
consistencyChecks := []consistencyCheck{
{
// find labels without existing repo or org
count, err := models.CountOrphanedLabels()
if err != nil {
logger.Critical("Error: %v whilst counting orphaned labels", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedLabels(); err != nil {
logger.Critical("Error: %v whilst deleting orphaned labels", err)
return err
}
logger.Info("%d labels without existing repository/organisation deleted", count)
} else {
logger.Warn("%d labels without existing repository/organisation", count)
}
}
Name: "Orphaned Labels without existing repository or organisation",
Counter: models.CountOrphanedLabels,
Fixer: asFixer(models.DeleteOrphanedLabels),
},
{
// find IssueLabels without existing label
count, err = models.CountOrphanedIssueLabels()
if err != nil {
logger.Critical("Error: %v whilst counting orphaned issue_labels", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedIssueLabels(); err != nil {
logger.Critical("Error: %v whilst deleting orphaned issue_labels", err)
return err
}
logger.Info("%d issue_labels without existing label deleted", count)
} else {
logger.Warn("%d issue_labels without existing label", count)
}
}
Name: "Orphaned Issue Labels without existing label",
Counter: models.CountOrphanedIssueLabels,
Fixer: asFixer(models.DeleteOrphanedIssueLabels),
},
{
// find issues without existing repository
count, err = models.CountOrphanedIssues()
if err != nil {
logger.Critical("Error: %v whilst counting orphaned issues", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedIssues(); err != nil {
logger.Critical("Error: %v whilst deleting orphaned issues", err)
return err
}
logger.Info("%d issues without existing repository deleted", count)
} else {
logger.Warn("%d issues without existing repository", count)
}
}
Name: "Orphaned Issues without existing repository",
Counter: models.CountOrphanedIssues,
Fixer: asFixer(models.DeleteOrphanedIssues),
},
// find releases without existing repository
genericOrphanCheck("Orphaned Releases without existing repository",
"release", "repository", "release.repo_id=repository.id"),
// find pulls without existing issues
count, err = models.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
if err != nil {
logger.Critical("Error: %v whilst counting orphaned objects", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id"); err != nil {
logger.Critical("Error: %v whilst deleting orphaned objects", err)
return err
}
logger.Info("%d pull requests without existing issue deleted", count)
} else {
logger.Warn("%d pull requests without existing issue", count)
}
}
genericOrphanCheck("Orphaned PullRequests without existing issue",
"pull_request", "issue", "pull_request.issue_id=issue.id"),
// find tracked times without existing issues/pulls
count, err = models.CountOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id")
if err != nil {
logger.Critical("Error: %v whilst counting orphaned objects", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id"); err != nil {
logger.Critical("Error: %v whilst deleting orphaned objects", err)
return err
}
logger.Info("%d tracked times without existing issue deleted", count)
} else {
logger.Warn("%d tracked times without existing issue", count)
}
}
genericOrphanCheck("Orphaned TrackedTimes without existing issue",
"tracked_time", "issue", "tracked_time.issue_id=issue.id"),
// find null archived repositories
count, err = models.CountNullArchivedRepository()
if err != nil {
logger.Critical("Error: %v whilst counting null archived repositories", err)
return err
}
if count > 0 {
if autofix {
updatedCount, err := models.FixNullArchivedRepository()
if err != nil {
logger.Critical("Error: %v whilst fixing null archived repositories", err)
return err
}
logger.Info("%d repositories with null is_archived updated", updatedCount)
} else {
logger.Warn("%d repositories with null is_archived", count)
}
}
{
Name: "Repositories with is_archived IS NULL",
Counter: models.CountNullArchivedRepository,
Fixer: models.FixNullArchivedRepository,
FixedMessage: "Fixed",
},
// find label comments with empty labels
count, err = models.CountCommentTypeLabelWithEmptyLabel()
if err != nil {
logger.Critical("Error: %v whilst counting label comments with empty labels", err)
return err
}
if count > 0 {
if autofix {
updatedCount, err := models.FixCommentTypeLabelWithEmptyLabel()
if err != nil {
logger.Critical("Error: %v whilst removing label comments with empty labels", err)
return err
}
logger.Info("%d label comments with empty labels removed", updatedCount)
} else {
logger.Warn("%d label comments with empty labels", count)
}
}
{
Name: "Label comments with empty labels",
Counter: models.CountCommentTypeLabelWithEmptyLabel,
Fixer: models.FixCommentTypeLabelWithEmptyLabel,
FixedMessage: "Fixed",
},
// find label comments with labels from outside the repository
count, err = models.CountCommentTypeLabelWithOutsideLabels()
if err != nil {
logger.Critical("Error: %v whilst counting label comments with outside labels", err)
return err
}
if count > 0 {
if autofix {
updatedCount, err := models.FixCommentTypeLabelWithOutsideLabels()
if err != nil {
logger.Critical("Error: %v whilst removing label comments with outside labels", err)
return err
}
log.Info("%d label comments with outside labels removed", updatedCount)
} else {
log.Warn("%d label comments with outside labels", count)
}
}
{
Name: "Label comments with labels from outside the repository",
Counter: models.CountCommentTypeLabelWithOutsideLabels,
Fixer: models.FixCommentTypeLabelWithOutsideLabels,
FixedMessage: "Removed",
},
// find issue_label with labels from outside the repository
count, err = models.CountIssueLabelWithOutsideLabels()
if err != nil {
logger.Critical("Error: %v whilst counting issue_labels from outside the repository or organisation", err)
return err
}
if count > 0 {
if autofix {
updatedCount, err := models.FixIssueLabelWithOutsideLabels()
if err != nil {
logger.Critical("Error: %v whilst removing issue_labels from outside the repository or organisation", err)
return err
}
logger.Info("%d issue_labels from outside the repository or organisation removed", updatedCount)
} else {
logger.Warn("%d issue_labels from outside the repository or organisation", count)
}
{
Name: "IssueLabels with Labels from outside the repository",
Counter: models.CountIssueLabelWithOutsideLabels,
Fixer: models.FixIssueLabelWithOutsideLabels,
FixedMessage: "Removed",
},
}
// TODO: function to recalc all counters
if setting.Database.UsePostgreSQL {
count, err = models.CountBadSequences()
if err != nil {
logger.Critical("Error: %v whilst checking sequence values", err)
return err
}
if count > 0 {
if autofix {
err := models.FixBadSequences()
if err != nil {
logger.Critical("Error: %v whilst attempting to fix sequences", err)
return err
}
logger.Info("%d sequences updated", count)
} else {
logger.Warn("%d sequences with incorrect values", count)
}
}
consistencyChecks = append(consistencyChecks, consistencyCheck{
Name: "Sequence values",
Counter: models.CountBadSequences,
Fixer: asFixer(models.FixBadSequences),
FixedMessage: "Updated",
})
}
consistencyChecks = append(consistencyChecks,
// find protected branches without existing repository
count, err = models.CountOrphanedObjects("protected_branch", "repository", "protected_branch.repo_id=repository.id")
if err != nil {
logger.Critical("Error: %v whilst counting orphaned objects", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedObjects("protected_branch", "repository", "protected_branch.repo_id=repository.id"); err != nil {
logger.Critical("Error: %v whilst deleting orphaned objects", err)
return err
}
logger.Info("%d protected branches without existing repository deleted", count)
} else {
logger.Warn("%d protected branches without existing repository", count)
}
}
genericOrphanCheck("Protected Branches without existing repository",
"protected_branch", "repository", "protected_branch.repo_id=repository.id"),
// find deleted branches without existing repository
count, err = models.CountOrphanedObjects("deleted_branch", "repository", "deleted_branch.repo_id=repository.id")
if err != nil {
logger.Critical("Error: %v whilst counting orphaned objects", err)
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedObjects("deleted_branch", "repository", "deleted_branch.repo_id=repository.id"); err != nil {
logger.Critical("Error: %v whilst deleting orphaned objects", err)
return err
}
logger.Info("%d deleted branches without existing repository deleted", count)
} else {
logger.Warn("%d deleted branches without existing repository", count)
}
}
genericOrphanCheck("Deleted Branches without existing repository",
"deleted_branch", "repository", "deleted_branch.repo_id=repository.id"),
// find LFS locks without existing repository
count, err = models.CountOrphanedObjects("lfs_lock", "repository", "lfs_lock.repo_id=repository.id")
if err != nil {
logger.Critical("Error: %v whilst counting orphaned objects", err)
genericOrphanCheck("LFS locks without existing repository",
"lfs_lock", "repository", "lfs_lock.repo_id=repository.id"),
// find collaborations without users
genericOrphanCheck("Collaborations without existing user",
"collaboration", "user", "collaboration.user_id=user.id"),
// find collaborations without repository
genericOrphanCheck("Collaborations without existing repository",
"collaboration", "repository", "collaboration.repo_id=repository.id"),
// find access without users
genericOrphanCheck("Access entries without existing user",
"access", "user", "access.user_id=user.id"),
// find access without repository
genericOrphanCheck("Access entries without existing repository",
"access", "repository", "access.repo_id=repository.id"),
)
for _, c := range consistencyChecks {
if err := c.Run(logger, autofix); err != nil {
return err
}
if count > 0 {
if autofix {
if err = models.DeleteOrphanedObjects("lfs_lock", "repository", "lfs_lock.repo_id=repository.id"); err != nil {
logger.Critical("Error: %v whilst deleting orphaned objects", err)
return err
}
logger.Info("%d LFS locks without existing repository deleted", count)
} else {
logger.Warn("%d LFS locks without existing repository", count)
}
}
return nil

317
modules/doctor/fix16961.go Normal file
View File

@@ -0,0 +1,317 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package doctor
import (
"bytes"
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)
// #16831 revealed that the dump command that was broken in 1.14.3-1.14.6 and 1.15.0 (#15885).
// This led to repo_unit and login_source cfg not being converted to JSON in the dump
// Unfortunately although it was hoped that there were only a few users affected it
// appears that many users are affected.
// We therefore need to provide a doctor command to fix this repeated issue #16961
func parseBool16961(bs []byte) (bool, error) {
if bytes.EqualFold(bs, []byte("%!s(bool=false)")) {
return false, nil
}
if bytes.EqualFold(bs, []byte("%!s(bool=true)")) {
return true, nil
}
return false, fmt.Errorf("unexpected bool format: %s", string(bs))
}
func fixUnitConfig16961(bs []byte, cfg *models.UnitConfig) (fixed bool, err error) {
err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err == nil {
return
}
// Handle #16961
if string(bs) != "&{}" && len(bs) != 0 {
return
}
return true, nil
}
func fixExternalWikiConfig16961(bs []byte, cfg *models.ExternalWikiConfig) (fixed bool, err error) {
err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err == nil {
return
}
if len(bs) < 3 {
return
}
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
return
}
cfg.ExternalWikiURL = string(bs[2 : len(bs)-1])
return true, nil
}
func fixExternalTrackerConfig16961(bs []byte, cfg *models.ExternalTrackerConfig) (fixed bool, err error) {
err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err == nil {
return
}
// Handle #16961
if len(bs) < 3 {
return
}
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
return
}
parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
if len(parts) != 3 {
return
}
cfg.ExternalTrackerURL = string(bytes.Join(parts[:len(parts)-2], []byte{' '}))
cfg.ExternalTrackerFormat = string(parts[len(parts)-2])
cfg.ExternalTrackerStyle = string(parts[len(parts)-1])
return true, nil
}
func fixPullRequestsConfig16961(bs []byte, cfg *models.PullRequestsConfig) (fixed bool, err error) {
err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err == nil {
return
}
// Handle #16961
if len(bs) < 3 {
return
}
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
return
}
// PullRequestsConfig was the following in 1.14
// type PullRequestsConfig struct {
// IgnoreWhitespaceConflicts bool
// AllowMerge bool
// AllowRebase bool
// AllowRebaseMerge bool
// AllowSquash bool
// AllowManualMerge bool
// AutodetectManualMerge bool
// }
//
// 1.15 added in addition:
// DefaultDeleteBranchAfterMerge bool
// DefaultMergeStyle MergeStyle
parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
if len(parts) < 7 {
return
}
var parseErr error
cfg.IgnoreWhitespaceConflicts, parseErr = parseBool16961(parts[0])
if parseErr != nil {
return
}
cfg.AllowMerge, parseErr = parseBool16961(parts[1])
if parseErr != nil {
return
}
cfg.AllowRebase, parseErr = parseBool16961(parts[2])
if parseErr != nil {
return
}
cfg.AllowRebaseMerge, parseErr = parseBool16961(parts[3])
if parseErr != nil {
return
}
cfg.AllowSquash, parseErr = parseBool16961(parts[4])
if parseErr != nil {
return
}
cfg.AllowManualMerge, parseErr = parseBool16961(parts[5])
if parseErr != nil {
return
}
cfg.AutodetectManualMerge, parseErr = parseBool16961(parts[6])
if parseErr != nil {
return
}
// 1.14 unit
if len(parts) == 7 {
return true, nil
}
if len(parts) < 9 {
return
}
cfg.DefaultDeleteBranchAfterMerge, parseErr = parseBool16961(parts[7])
if parseErr != nil {
return
}
cfg.DefaultMergeStyle = models.MergeStyle(string(bytes.Join(parts[8:], []byte{' '})))
return true, nil
}
func fixIssuesConfig16961(bs []byte, cfg *models.IssuesConfig) (fixed bool, err error) {
err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err == nil {
return
}
// Handle #16961
if len(bs) < 3 {
return
}
if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
return
}
parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
if len(parts) != 3 {
return
}
var parseErr error
cfg.EnableTimetracker, parseErr = parseBool16961(parts[0])
if parseErr != nil {
return
}
cfg.AllowOnlyContributorsToTrackTime, parseErr = parseBool16961(parts[1])
if parseErr != nil {
return
}
cfg.EnableDependencies, parseErr = parseBool16961(parts[2])
if parseErr != nil {
return
}
return true, nil
}
func fixBrokenRepoUnit16961(repoUnit *models.RepoUnit, bs []byte) (fixed bool, err error) {
// Shortcut empty or null values
if len(bs) == 0 {
return false, nil
}
switch models.UnitType(repoUnit.Type) {
case models.UnitTypeCode, models.UnitTypeReleases, models.UnitTypeWiki, models.UnitTypeProjects:
cfg := &models.UnitConfig{}
repoUnit.Config = cfg
if fixed, err := fixUnitConfig16961(bs, cfg); !fixed {
return false, err
}
case models.UnitTypeExternalWiki:
cfg := &models.ExternalWikiConfig{}
repoUnit.Config = cfg
if fixed, err := fixExternalWikiConfig16961(bs, cfg); !fixed {
return false, err
}
case models.UnitTypeExternalTracker:
cfg := &models.ExternalTrackerConfig{}
repoUnit.Config = cfg
if fixed, err := fixExternalTrackerConfig16961(bs, cfg); !fixed {
return false, err
}
case models.UnitTypePullRequests:
cfg := &models.PullRequestsConfig{}
repoUnit.Config = cfg
if fixed, err := fixPullRequestsConfig16961(bs, cfg); !fixed {
return false, err
}
case models.UnitTypeIssues:
cfg := &models.IssuesConfig{}
repoUnit.Config = cfg
if fixed, err := fixIssuesConfig16961(bs, cfg); !fixed {
return false, err
}
default:
panic(fmt.Sprintf("unrecognized repo unit type: %v", repoUnit.Type))
}
return true, nil
}
func fixBrokenRepoUnits16961(logger log.Logger, autofix bool) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
ID int64
RepoID int64
Type models.UnitType
Config []byte
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
}
count := 0
err := models.Iterate(
models.DefaultDBContext(),
new(RepoUnit),
builder.Gt{
"id": 0,
},
func(idx int, bean interface{}) error {
unit := bean.(*RepoUnit)
bs := unit.Config
repoUnit := &models.RepoUnit{
ID: unit.ID,
RepoID: unit.RepoID,
Type: unit.Type,
CreatedUnix: unit.CreatedUnix,
}
if fixed, err := fixBrokenRepoUnit16961(repoUnit, bs); !fixed {
return err
}
count++
if !autofix {
return nil
}
return models.UpdateRepoUnit(repoUnit)
},
)
if err != nil {
logger.Critical("Unable to iterate acrosss repounits to fix the broken units: Error %v", err)
return err
}
if !autofix {
logger.Warn("Found %d broken repo_units", count)
return nil
}
logger.Info("Fixed %d broken repo_units", count)
return nil
}
func init() {
Register(&Check{
Title: "Check for incorrectly dumped repo_units (See #16961)",
Name: "fix-broken-repo-units",
IsDefault: false,
Run: fixBrokenRepoUnits16961,
Priority: 7,
})
}

View File

@@ -0,0 +1,271 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package doctor
import (
"testing"
"code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
)
func Test_fixUnitConfig_16961(t *testing.T) {
tests := []struct {
name string
bs string
wantFixed bool
wantErr bool
}{
{
name: "empty",
bs: "",
wantFixed: true,
wantErr: false,
},
{
name: "normal: {}",
bs: "{}",
wantFixed: false,
wantErr: false,
},
{
name: "broken but fixable: &{}",
bs: "&{}",
wantFixed: true,
wantErr: false,
},
{
name: "broken but unfixable: &{asdasd}",
bs: "&{asdasd}",
wantFixed: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotFixed, err := fixUnitConfig16961([]byte(tt.bs), &models.UnitConfig{})
if (err != nil) != tt.wantErr {
t.Errorf("fixUnitConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotFixed != tt.wantFixed {
t.Errorf("fixUnitConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
})
}
}
func Test_fixExternalWikiConfig_16961(t *testing.T) {
tests := []struct {
name string
bs string
expected string
wantFixed bool
wantErr bool
}{
{
name: "normal: {\"ExternalWikiURL\":\"http://someurl\"}",
bs: "{\"ExternalWikiURL\":\"http://someurl\"}",
expected: "http://someurl",
wantFixed: false,
wantErr: false,
},
{
name: "broken: &{http://someurl}",
bs: "&{http://someurl}",
expected: "http://someurl",
wantFixed: true,
wantErr: false,
},
{
name: "broken but unfixable: http://someurl",
bs: "http://someurl",
wantFixed: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &models.ExternalWikiConfig{}
gotFixed, err := fixExternalWikiConfig16961([]byte(tt.bs), cfg)
if (err != nil) != tt.wantErr {
t.Errorf("fixExternalWikiConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotFixed != tt.wantFixed {
t.Errorf("fixExternalWikiConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
if cfg.ExternalWikiURL != tt.expected {
t.Errorf("fixExternalWikiConfig_16961().ExternalWikiURL = %v, want %v", cfg.ExternalWikiURL, tt.expected)
}
})
}
}
func Test_fixExternalTrackerConfig_16961(t *testing.T) {
tests := []struct {
name string
bs string
expected models.ExternalTrackerConfig
wantFixed bool
wantErr bool
}{
{
name: "normal",
bs: `{"ExternalTrackerURL":"a","ExternalTrackerFormat":"b","ExternalTrackerStyle":"c"}`,
expected: models.ExternalTrackerConfig{
ExternalTrackerURL: "a",
ExternalTrackerFormat: "b",
ExternalTrackerStyle: "c",
},
wantFixed: false,
wantErr: false,
},
{
name: "broken",
bs: "&{a b c}",
expected: models.ExternalTrackerConfig{
ExternalTrackerURL: "a",
ExternalTrackerFormat: "b",
ExternalTrackerStyle: "c",
},
wantFixed: true,
wantErr: false,
},
{
name: "broken - too many fields",
bs: "&{a b c d}",
wantFixed: false,
wantErr: true,
},
{
name: "broken - wrong format",
bs: "a b c d}",
wantFixed: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &models.ExternalTrackerConfig{}
gotFixed, err := fixExternalTrackerConfig16961([]byte(tt.bs), cfg)
if (err != nil) != tt.wantErr {
t.Errorf("fixExternalTrackerConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotFixed != tt.wantFixed {
t.Errorf("fixExternalTrackerConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
if cfg.ExternalTrackerFormat != tt.expected.ExternalTrackerFormat {
t.Errorf("fixExternalTrackerConfig_16961().ExternalTrackerFormat = %v, want %v", tt.expected.ExternalTrackerFormat, cfg.ExternalTrackerFormat)
}
if cfg.ExternalTrackerStyle != tt.expected.ExternalTrackerStyle {
t.Errorf("fixExternalTrackerConfig_16961().ExternalTrackerStyle = %v, want %v", tt.expected.ExternalTrackerStyle, cfg.ExternalTrackerStyle)
}
if cfg.ExternalTrackerURL != tt.expected.ExternalTrackerURL {
t.Errorf("fixExternalTrackerConfig_16961().ExternalTrackerURL = %v, want %v", tt.expected.ExternalTrackerURL, cfg.ExternalTrackerURL)
}
})
}
}
func Test_fixPullRequestsConfig_16961(t *testing.T) {
tests := []struct {
name string
bs string
expected models.PullRequestsConfig
wantFixed bool
wantErr bool
}{
{
name: "normal",
bs: `{"IgnoreWhitespaceConflicts":false,"AllowMerge":false,"AllowRebase":false,"AllowRebaseMerge":false,"AllowSquash":false,"AllowManualMerge":false,"AutodetectManualMerge":false,"DefaultDeleteBranchAfterMerge":false,"DefaultMergeStyle":""}`,
},
{
name: "broken - 1.14",
bs: `&{%!s(bool=false) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=false) %!s(bool=false)}`,
expected: models.PullRequestsConfig{
IgnoreWhitespaceConflicts: false,
AllowMerge: true,
AllowRebase: true,
AllowRebaseMerge: true,
AllowSquash: true,
AllowManualMerge: false,
AutodetectManualMerge: false,
},
wantFixed: true,
},
{
name: "broken - 1.15",
bs: `&{%!s(bool=false) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=false) %!s(bool=false) %!s(bool=false) merge}`,
expected: models.PullRequestsConfig{
AllowMerge: true,
AllowRebase: true,
AllowRebaseMerge: true,
AllowSquash: true,
DefaultMergeStyle: models.MergeStyleMerge,
},
wantFixed: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &models.PullRequestsConfig{}
gotFixed, err := fixPullRequestsConfig16961([]byte(tt.bs), cfg)
if (err != nil) != tt.wantErr {
t.Errorf("fixPullRequestsConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotFixed != tt.wantFixed {
t.Errorf("fixPullRequestsConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
assert.EqualValues(t, &tt.expected, cfg)
})
}
}
func Test_fixIssuesConfig_16961(t *testing.T) {
tests := []struct {
name string
bs string
expected models.IssuesConfig
wantFixed bool
wantErr bool
}{
{
name: "normal",
bs: `{"EnableTimetracker":true,"AllowOnlyContributorsToTrackTime":true,"EnableDependencies":true}`,
expected: models.IssuesConfig{
EnableTimetracker: true,
AllowOnlyContributorsToTrackTime: true,
EnableDependencies: true,
},
},
{
name: "broken",
bs: `&{%!s(bool=true) %!s(bool=true) %!s(bool=true)}`,
expected: models.IssuesConfig{
EnableTimetracker: true,
AllowOnlyContributorsToTrackTime: true,
EnableDependencies: true,
},
wantFixed: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &models.IssuesConfig{}
gotFixed, err := fixIssuesConfig16961([]byte(tt.bs), cfg)
if (err != nil) != tt.wantErr {
t.Errorf("fixIssuesConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotFixed != tt.wantFixed {
t.Errorf("fixIssuesConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
assert.EqualValues(t, &tt.expected, cfg)
})
}
}

76
modules/doctor/storage.go Normal file
View File

@@ -0,0 +1,76 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package doctor
import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
)
func checkAttachmentStorageFiles(logger log.Logger, autofix bool) error {
var total, garbageNum int
var deletePaths []string
if err := storage.Attachments.IterateObjects(func(p string, obj storage.Object) error {
defer obj.Close()
total++
stat, err := obj.Stat()
if err != nil {
return err
}
exist, err := models.ExistAttachmentsByUUID(stat.Name())
if err != nil {
return err
}
if !exist {
garbageNum++
if autofix {
deletePaths = append(deletePaths, p)
}
}
return nil
}); err != nil {
logger.Error("storage.Attachments.IterateObjects failed: %v", err)
return err
}
if garbageNum > 0 {
if autofix {
var deletedNum int
for _, p := range deletePaths {
if err := storage.Attachments.Delete(p); err != nil {
log.Error("Delete attachment %s failed: %v", p, err)
} else {
deletedNum++
}
}
logger.Info("%d missed information attachment detected, %d deleted.", garbageNum, deletedNum)
} else {
logger.Warn("Checked %d attachment, %d missed information.", total, garbageNum)
}
}
return nil
}
func checkStorageFiles(logger log.Logger, autofix bool) error {
if err := storage.Init(); err != nil {
logger.Error("storage.Init failed: %v", err)
return err
}
return checkAttachmentStorageFiles(logger, autofix)
}
func init() {
Register(&Check{
Title: "Check if there is garbage storage files",
Name: "storages",
IsDefault: false,
Run: checkStorageFiles,
AbortIfFailed: false,
SkipDatabaseInitialization: false,
Priority: 1,
})
}

View File

@@ -8,8 +8,10 @@ import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"math"
"runtime"
"strconv"
"strings"
@@ -40,9 +42,14 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()
<-closed
}
_, filename, line, _ := runtime.Caller(2)
filename = strings.TrimPrefix(filename, callerPrefix)
go func() {
stderr := strings.Builder{}
err := NewCommandContext(ctx, "cat-file", "--batch-check").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
err := NewCommandContext(ctx, "cat-file", "--batch-check").
SetDescription(fmt.Sprintf("%s cat-file --batch-check [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
@@ -76,9 +83,14 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
<-closed
}
_, filename, line, _ := runtime.Caller(2)
filename = strings.TrimPrefix(filename, callerPrefix)
go func() {
stderr := strings.Builder{}
err := NewCommandContext(ctx, "cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
err := NewCommandContext(ctx, "cat-file", "--batch").
SetDescription(fmt.Sprintf("%s cat-file --batch [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
@@ -292,3 +304,10 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
sha = shaBuf
return
}
var callerPrefix string
func init() {
_, filename, _, _ := runtime.Caller(0)
callerPrefix = strings.TrimSuffix(filename, "modules/git/batch_reader.go")
}

View File

@@ -18,11 +18,16 @@ import (
)
// LogNameStatusRepo opens git log --raw in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func LogNameStatusRepo(repository, head, treepath string, paths ...string) (*bufio.Reader, func()) {
func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, paths ...string) (*bufio.Reader, func()) {
// We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout
stdoutReader, stdoutWriter := nio.Pipe(buffer.New(32 * 1024))
// Lets also create a context so that we can absolutely ensure that the command should die when we're done
ctx, ctxCancel := context.WithCancel(ctx)
cancel := func() {
ctxCancel()
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}
@@ -50,7 +55,7 @@ func LogNameStatusRepo(repository, head, treepath string, paths ...string) (*buf
go func() {
stderr := strings.Builder{}
err := NewCommand(args...).RunInDirFullPipeline(repository, stdoutWriter, &stderr, nil)
err := NewCommandContext(ctx, args...).RunInDirFullPipeline(repository, stdoutWriter, &stderr, nil)
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
} else {
@@ -75,8 +80,8 @@ type LogNameStatusRepoParser struct {
}
// NewLogNameStatusRepoParser returns a new parser for a git log raw output
func NewLogNameStatusRepoParser(repository, head, treepath string, paths ...string) *LogNameStatusRepoParser {
rd, cancel := LogNameStatusRepo(repository, head, treepath, paths...)
func NewLogNameStatusRepoParser(ctx context.Context, repository, head, treepath string, paths ...string) *LogNameStatusRepoParser {
rd, cancel := LogNameStatusRepo(ctx, repository, head, treepath, paths...)
return &LogNameStatusRepoParser{
treepath: treepath,
paths: paths,
@@ -311,8 +316,11 @@ func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath st
}
}
g := NewLogNameStatusRepoParser(repo.Path, head.ID.String(), treepath, paths...)
defer g.Close()
g := NewLogNameStatusRepoParser(ctx, repo.Path, head.ID.String(), treepath, paths...)
// don't use defer g.Close() here as g may change its value - instead wrap in a func
defer func() {
g.Close()
}()
results := make([]string, len(paths))
remaining := len(paths)
@@ -331,6 +339,7 @@ heaploop:
for {
select {
case <-ctx.Done():
g.Close()
return nil, ctx.Err()
default:
}
@@ -380,7 +389,7 @@ heaploop:
remainingPaths = append(remainingPaths, pth)
}
}
g = NewLogNameStatusRepoParser(repo.Path, lastEmptyParent, treepath, remainingPaths...)
g = NewLogNameStatusRepoParser(ctx, repo.Path, lastEmptyParent, treepath, remainingPaths...)
parentRemaining = map[string]bool{}
nextRestart = (remaining * 3) / 4
continue heaploop

View File

@@ -66,17 +66,6 @@ func Code(fileName, code string) string {
if len(code) > sizeLimit {
return code
}
formatter := html.New(html.WithClasses(true),
html.WithLineNumbers(false),
html.PreventSurroundingPre(true),
)
if formatter == nil {
log.Error("Couldn't create chroma formatter")
return code
}
htmlbuf := bytes.Buffer{}
htmlw := bufio.NewWriter(&htmlbuf)
var lexer chroma.Lexer
if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
@@ -97,6 +86,18 @@ func Code(fileName, code string) string {
}
cache.Add(fileName, lexer)
}
return CodeFromLexer(lexer, code)
}
// CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes
func CodeFromLexer(lexer chroma.Lexer, code string) string {
formatter := html.New(html.WithClasses(true),
html.WithLineNumbers(false),
html.PreventSurroundingPre(true),
)
htmlbuf := bytes.Buffer{}
htmlw := bufio.NewWriter(&htmlbuf)
iterator, err := lexer.Tokenise(nil, string(code))
if err != nil {

View File

@@ -827,7 +827,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
if exttrack && !ref.IsPull {
ctx.Metas["index"] = ref.Issue
link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue")
link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue ref-external-issue")
} else {
// Path determines the type of link that will be rendered. It's unknown at this point whether
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because

View File

@@ -96,12 +96,14 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
// numeric: render inputs with valid mentions
test := func(s, expectedFmt, marker string, indices ...int) {
var path, prefix string
isExternal := false
if marker == "!" {
path = "pulls"
prefix = "http://localhost:3000/someUser/someRepo/pulls/"
} else {
path = "issues"
prefix = "https://someurl.com/someUser/someRepo/"
isExternal = true
}
links := make([]interface{}, len(indices))
@@ -111,8 +113,13 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
expectedNil := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{Metas: localMetas})
class := "ref-issue"
if isExternal {
class += " ref-external-issue"
}
for i, index := range indices {
links[i] = numericIssueLink(prefix, "ref-issue", index, marker)
links[i] = numericIssueLink(prefix, class, index, marker)
}
expectedNum := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{Metas: numericMetas})
@@ -178,7 +185,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
test := func(s, expectedFmt string, names ...string) {
links := make([]interface{}, len(names))
for i, name := range names {
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue", name)
links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
}
expected := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: alphanumericMetas})

View File

@@ -12,6 +12,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -51,6 +52,12 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
htmlWriter := org.NewHTMLWriter()
htmlWriter.HighlightCodeBlock = func(source, lang string, inline bool) string {
defer func() {
if err := recover(); err != nil {
log.Error("Panic in HighlightCodeBlock: %v\n%s", err, log.Stack(2))
panic(err)
}
}()
var w strings.Builder
if _, err := w.WriteString(`<pre>`); err != nil {
return ""
@@ -80,7 +87,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
}
lexer = chroma.Coalesce(lexer)
if _, err := w.WriteString(highlight.Code(lexer.Config().Filenames[0], source)); err != nil {
if _, err := w.WriteString(highlight.CodeFromLexer(lexer, source)); err != nil {
return ""
}
}

View File

@@ -57,3 +57,29 @@ func TestRender_Images(t *testing.T) {
test("[[file:"+url+"]]",
"<p><img src=\""+result+"\" alt=\""+result+"\" title=\""+result+"\" /></p>")
}
func TestRender_Source(t *testing.T) {
setting.AppURL = AppURL
setting.AppSubURL = AppSubURL
test := func(input, expected string) {
buffer, err := RenderString(&markup.RenderContext{
URLPrefix: setting.AppSubURL,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test(`#+begin_src go
// HelloWorld prints "Hello World"
func HelloWorld() {
fmt.Println("Hello World")
}
#+end_src
`, `<div class="src src-go">
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints &#34;Hello World&#34;
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello World&#34;</span><span class="p">)</span>
<span class="p">}</span></code></pre>
</div>`)
}

View File

@@ -66,7 +66,7 @@ func createDefaultPolicy() *bluemonday.Policy {
}
// Allow classes for anchors
policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue( ref-external-issue)?`)).OnElements("a")
// Allow classes for task lists
policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")

View File

@@ -541,6 +541,9 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
created = "created"
asc = "asc"
)
if perPage > g.maxPerPage {
perPage = g.maxPerPage
}
opt := &github.IssueListCommentsOptions{
Sort: &created,
Direction: &asc,
@@ -555,7 +558,9 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
if err != nil {
return nil, false, fmt.Errorf("error while listing repos: %v", err)
}
log.Trace("Request get comments %d/%d, but in fact get %d", perPage, page, len(comments))
var isEnd = resp.NextPage == 0
log.Trace("Request get comments %d/%d, but in fact get %d, next page is %d", perPage, page, len(comments), resp.NextPage)
g.rate = &resp.Rate
for _, comment := range comments {
var email string
@@ -600,7 +605,7 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
})
}
return allComments, len(allComments) < perPage, nil
return allComments, isEnd, nil
}
// GetPullRequests returns pull requests according page and perPage

View File

@@ -51,7 +51,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model
err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelCleared,
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -145,7 +145,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo
issue.PullRequest.Issue = issue
apiPullRequest := &api.PullRequestPayload{
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
}
@@ -197,7 +197,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
From: oldTitle,
},
},
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -232,7 +232,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
// Merge pull request calls issue.changeStatus so we need to handle separately.
apiPullRequest := &api.PullRequestPayload{
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
}
@@ -301,7 +301,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest, mention
if err := webhook_services.PrepareWebhooks(pull.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueOpened,
Index: pull.Issue.Index,
PullRequest: convert.ToAPIPullRequest(pull),
PullRequest: convert.ToAPIPullRequest(pull, nil),
Repository: convert.ToRepo(pull.Issue.Repo, mode),
Sender: convert.ToUser(pull.Issue.Poster, nil),
}); err != nil {
@@ -322,7 +322,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
From: oldContent,
},
},
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -500,7 +500,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode
err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelUpdated,
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, models.AccessModeNone),
Sender: convert.ToUser(doer, nil),
})
@@ -542,7 +542,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestMilestone, &api.PullRequestPayload{
Action: hookAction,
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -609,7 +609,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mod
// Merge pull request calls issue.changeStatus so we need to handle separately.
apiPullRequest := &api.PullRequestPayload{
Index: pr.Issue.Index,
PullRequest: convert.ToAPIPullRequest(pr),
PullRequest: convert.ToAPIPullRequest(pr, nil),
Repository: convert.ToRepo(pr.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
Action: api.HookIssueClosed,
@@ -642,7 +642,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User,
From: oldBranch,
},
},
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -681,7 +681,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
if err := webhook_services.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
Action: api.HookIssueReviewed,
Index: review.Issue.Index,
PullRequest: convert.ToAPIPullRequest(pr),
PullRequest: convert.ToAPIPullRequest(pr, nil),
Repository: convert.ToRepo(review.Issue.Repo, mode),
Sender: convert.ToUser(review.Reviewer, nil),
Review: &api.ReviewPayload{
@@ -736,7 +736,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *m
if err := webhook_services.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequestSync, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: pr.Issue.Index,
PullRequest: convert.ToAPIPullRequest(pr),
PullRequest: convert.ToAPIPullRequest(pr, nil),
Repository: convert.ToRepo(pr.Issue.Repo, models.AccessModeNone),
Sender: convert.ToUser(doer, nil),
}); err != nil {

View File

@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
)
// ForkRepository forks a repository
@@ -45,38 +46,60 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
oldRepoPath := oldRepo.RepoPath()
needsRollback := false
rollbackFn := func() {
if !needsRollback {
return
}
repoPath := models.RepoPath(owner.Name, repo.Name)
if exists, _ := util.IsExist(repoPath); !exists {
return
}
// As the transaction will be failed and hence database changes will be destroyed we only need
// to delete the related repository on the filesystem
if errDelete := util.RemoveAll(repoPath); errDelete != nil {
log.Error("Failed to remove fork repo")
}
}
needsRollbackInPanic := true
defer func() {
panicErr := recover()
if panicErr == nil {
return
}
if needsRollbackInPanic {
rollbackFn()
}
panic(panicErr)
}()
err = models.WithTx(func(ctx models.DBContext) error {
if err = models.CreateRepository(ctx, doer, owner, repo, false); err != nil {
return err
}
rollbackRemoveFn := func() {
if repo.ID == 0 {
return
}
if errDelete := models.DeleteRepository(doer, owner.ID, repo.ID); errDelete != nil {
log.Error("Rollback deleteRepository: %v", errDelete)
}
}
if err = models.IncrementRepoForkNum(ctx, oldRepo.ID); err != nil {
rollbackRemoveFn()
return err
}
// copy lfs files failure should not be ignored
if err := models.CopyLFS(ctx, repo, oldRepo); err != nil {
rollbackRemoveFn()
if err = models.CopyLFS(ctx, repo, oldRepo); err != nil {
return err
}
needsRollback = true
repoPath := models.RepoPath(owner.Name, repo.Name)
if stdout, err := git.NewCommand(
"clone", "--bare", oldRepoPath, repoPath).
SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", oldRepo.FullName(), repo.FullName())).
RunInDirTimeout(10*time.Minute, ""); err != nil {
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, oldRepo, stdout, err)
rollbackRemoveFn()
return fmt.Errorf("git clone: %v", err)
}
@@ -84,23 +107,23 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())).
RunInDir(repoPath); err != nil {
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
rollbackRemoveFn()
return fmt.Errorf("git update-server-info: %v", err)
}
if err = createDelegateHooks(repoPath); err != nil {
rollbackRemoveFn()
return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
})
needsRollbackInPanic = false
if err != nil {
rollbackFn()
return nil, err
}
// even if below operations failed, it could be ignored. And they will be retried
ctx := models.DefaultDBContext()
if err = repo.UpdateSize(ctx); err != nil {
if err := repo.UpdateSize(ctx); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
if err := models.CopyLanguageStat(oldRepo, repo); err != nil {
@@ -109,3 +132,34 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
return repo, nil
}
// ConvertForkToNormalRepository convert the provided repo from a forked repo to normal repo
func ConvertForkToNormalRepository(repo *models.Repository) error {
err := models.WithTx(func(ctx models.DBContext) error {
repo, err := models.GetRepositoryByIDCtx(ctx, repo.ID)
if err != nil {
return err
}
if !repo.IsFork {
return nil
}
if err := models.DecrementRepoForkNum(ctx, repo.ForkID); err != nil {
log.Error("Unable to decrement repo fork num for old root repo %d of repository %-v whilst converting from fork. Error: %v", repo.ForkID, repo, err)
return err
}
repo.IsFork = false
repo.ForkID = 0
if err := models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
log.Error("Unable to update repository %-v whilst converting from fork. Error: %v", repo, err)
return err
}
return nil
})
return err
}

View File

@@ -151,7 +151,7 @@ type minioFileInfo struct {
}
func (m minioFileInfo) Name() string {
return m.ObjectInfo.Key
return path.Base(m.ObjectInfo.Key)
}
func (m minioFileInfo) Size() int64 {
@@ -219,7 +219,7 @@ func (m *MinioStorage) IterateObjects(fn func(path string, obj Object) error) er
}
if err := func(object *minio.Object, fn func(path string, obj Object) error) error {
defer object.Close()
return fn(strings.TrimPrefix(m.basePath, mObjInfo.Key), &minioObject{object})
return fn(strings.TrimPrefix(mObjInfo.Key, m.basePath), &minioObject{object})
}(object, fn); err != nil {
return convertMinioErr(err)
}

View File

@@ -93,7 +93,6 @@ func runMigrateTask(t *models.Task) (err error) {
}
opts.MigrateToRepoID = t.RepoID
var repo *models.Repository
ctx, cancel := context.WithCancel(graceful.GetManager().ShutdownContext())
defer cancel()
@@ -107,7 +106,7 @@ func runMigrateTask(t *models.Task) (err error) {
return
}
repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
message := models.TranslatableMessage{
Format: format,
Args: args,
@@ -118,7 +117,7 @@ func runMigrateTask(t *models.Task) (err error) {
_ = t.UpdateCols("message")
})
if err == nil {
log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
log.Trace("Repository migrated [%d]: %s/%s", t.Repo.ID, t.Owner.Name, t.Repo.Name)
return
}

View File

@@ -534,7 +534,7 @@ migrate.clone_address_desc=HTTP(S) или Git URL за клониране на
migrate.clone_local_path=или път към локален сървър
migrate.permission_denied=Недостатъчни права за импорт на локални хранилища.
migrate.failed=Грешка при миграция: %v
migrated_from_fake=Мигриран от %[1]с
migrated_from_fake=Мигриран от %[1]s
migrate.migrating=Мигриране от <b>%s</b>...
migrate.migrating_failed=Мигрирането от <b>%s</b> беше неуспешно.

View File

@@ -326,7 +326,7 @@ hi_user_x=Hallo <b>%s</b>,
activate_account=Bitte aktiviere dein Konto
activate_account.title=%s, bitte aktiviere dein Konto
activate_account.text_1=Hallo <b>%[1]s</b>, danke für deine Registrierung bei %[2]!
activate_account.text_1=Hallo <b>%[1]s</b>, danke für deine Registrierung bei %[2]s!
activate_account.text_2=Bitte klicke innerhalb von <b>%s</b> auf folgenden Link, um dein Konto zu aktivieren:
activate_email=Bestätige deine E-Mail-Adresse

View File

@@ -345,8 +345,8 @@ reset_password.text = Please click the following link to recover your account wi
register_success = Registration successful
issue_assigned.pull = @%[1]s assigned you to the pull request %[2]s in repository %[3]s.
issue_assigned.issue = @%[1]s assigned you to the issue %[2]s in repository %[3]s.
issue_assigned.pull = @%[1]s assigned you to pull request %[2]s in repository %[3]s.
issue_assigned.issue = @%[1]s assigned you to issue %[2]s in repository %[3]s.
issue.x_mentioned_you = <b>@%s</b> mentioned you:
issue.action.force_push = <b>%[1]s</b> force-pushed the <b>%[2]s</b> from %[3]s to %[4]s.

View File

@@ -326,7 +326,7 @@ hi_user_x=Hola <b>%s</b>,
activate_account=Por favor, active su cuenta
activate_account.title=%s, por favor activa tu cuenta
activate_account.text_1=¡Hola <b>%[1]s</b>, gracias por registrarse en %[2]!
activate_account.text_1=¡Hola <b>%[1]s</b>, gracias por registrarse en %[2]s!
activate_account.text_2=Por favor, haga clic en el siguiente enlace para activar su cuenta dentro de <b>%s</b>:
activate_email=Verifique su correo electrónico

View File

@@ -1048,7 +1048,7 @@ issues.action_assignee=Toegewezene
issues.action_assignee_no_select=Geen verantwoordelijke
issues.opened_by=%[1]s geopend door <a href="%[2]s">%[3]s</a>
issues.closed_by=door <a href="%[2]s">%[3]s</a> gesloten %[1]s
issues.closed_by_fake=met %[2]gesloten %[1]s
issues.closed_by_fake=met %[2]s gesloten %[1]s
issues.previous=Vorige
issues.next=Volgende
issues.open_title=Open

View File

@@ -2245,7 +2245,7 @@ dashboard.task.cancelled=Tarefa: %[1]s cancelada: %[3]s
dashboard.task.error=Erro na tarefa: %[1]s: %[3]s
dashboard.task.finished=Tarefa: %[1]s iniciada por %[2]s foi concluída
dashboard.task.unknown=Tarefa desconhecida: %[1]s
dashboard.cron.started=Cron iniciado: %[1]
dashboard.cron.started=Cron iniciado: %[1]s
dashboard.cron.process=Cron: %[1]s
dashboard.cron.cancelled=Cron: %s cancelado: %[3]s
dashboard.cron.error=Erro no cron: %s: %[3]s
@@ -2698,7 +2698,7 @@ notices.delete_success=As notificações do sistema foram eliminadas.
[action]
create_repo=criou o repositório <a href="%s">%s</a>
rename_repo=renomeou o repositório de <code>%[1]s</code> para <a href="%[2]s">%[3]</a>
rename_repo=renomeou o repositório de <code>%[1]s</code> para <a href="%[2]s">%[3]s</a>
commit_repo=enviou para <a href="%[1]s/src/branch/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
create_issue=`abriu a questão <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`fechou a questão <a href="%s/issues/%s">%s#%[2]s</a>`
@@ -2724,7 +2724,7 @@ reject_pull_request=`sugeriu modificações para <a href="%s/pulls/%s">%s#%[2]s<
publish_release=`lançou <a href="%s/releases/tag/%s"> "%[4]s" </a> à <a href="%[1]s">%[3]s</a>`
review_dismissed=`descartou a revisão de <b>%[4]s</b> para <a href="%[1]s/pulls/%[2]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Motivo:
create_branch=criou o ramo <a href="%[1]s/src/branch/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]</a>
create_branch=criou o ramo <a href="%[1]s/src/branch/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
[tool]
ago=há %s

View File

@@ -334,7 +334,7 @@ activate_email.title=%s, пожалуйста, подтвердите ваш а
activate_email.text=Пожалуйста, перейдите по ссылке, чтобы подтвердить ваш адрес электронной почты в течение <b>%s</b>:
register_notify=Добро пожаловать на Gitea
register_notify.title=%[1], добро пожаловать в %[2]
register_notify.title=%[1]s, добро пожаловать в %[2]s
register_notify.text_1=это письмо с вашим подтверждением регистрации в %s!
register_notify.text_2=Теперь вы можете войти через логин: %s.
register_notify.text_3=Если эта учетная запись была создана для вас, пожалуйста, сначала <a href="%s">установите пароль</a>.
@@ -345,7 +345,7 @@ reset_password.text=Пожалуйста, перейдите по ссылке,
register_success=Регистрация прошла успешно
issue_assigned.pull=@%[1] назначил вам запрос на слияние %[2] в репозитории %[3].
issue_assigned.pull=@%[1]s назначил вам запрос на слияние %[2]s в репозитории %[3]s.
issue_assigned.issue=@%[1]s назначил вам задачу %[2]s в репозитории %[3]s.
issue.x_mentioned_you=<b>@%s</b> упомянул вас:
@@ -1219,8 +1219,8 @@ issues.reopened_at=`переоткрыл(а) эту проблему <a id="%[1]
issues.commit_ref_at=`упомянул эту задачу в коммите <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">ссылка на эту проблему %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">ссылается на этот Pull Request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">ссылается на Pull Request %[4], который закроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">ссылается на Pull Request %[4], который вновь откроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">ссылается на Pull Request %[4]s, который закроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">ссылается на Pull Request %[4]s, который вновь откроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closed_from=`<a href="%[3]s">закрыл этот запрос %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">переоткрыл эту задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`из %[1]s`
@@ -1303,7 +1303,7 @@ issues.error_modifying_due_date=Не удалось изменить срок в
issues.error_removing_due_date=Не удалось убрать срок выполнения.
issues.push_commit_1=добавил(а) %d коммит %s
issues.push_commits_n=добавил(а) %d коммитов %s
issues.force_push_codes=`принудительно залито %[1]s от <a class="ui sha" href="%[3]s"><code>%[2]</code></a> к <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.force_push_codes=`принудительно залито %[1]s от <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> к <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.due_date_form=гггг-мм-дд
issues.due_date_form_add=Добавить срок выполнения
issues.due_date_form_edit=Редактировать

View File

@@ -1147,7 +1147,7 @@ issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> konusunu yeniden açt
issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu konuyu işaret etti`
issues.ref_issue_from=`<a href="%[3]s">bu konuya referansta bulundu %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">bu değişiklik isteğine referansta bulundu %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">bir değişiklik isteğine referansta bulundu %[4] bu konu kapatılacak </a><a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">bir değişiklik isteğine referansta bulundu %[4]s bu konu kapatılacak </a><a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">bir değişiklik isteğine referansta bulundu %[4]s bu konu yeniden açılacak</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closed_from=`<a href="%[3]s">bu konuyu kapat%[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">konuyu yeniden aç%[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`

View File

@@ -348,7 +348,7 @@ issue.x_mentioned_you=<b>@%s</b> згадав вас:
issue.action.force_push=<b>%[1]s</b> force-pushed <b>%[2]s</b> з %[3]s в %[4]s.
issue.action.push_n=<b>@%[1]s</b> відправив %[3]d коміти до %[2]s
issue.action.close=<b>@%[1]s</b> закрито #%[2]d.
issue.action.reopen=<b>@%[1]</b> заново відкрив #%[2]d.
issue.action.reopen=<b>@%[1]s</b> заново відкрив #%[2]d.
issue.action.merge=<b>@%[1]s</b> об'єднав #%[2]d до %[3]s.
issue.action.approve=<b>@%[1]s</b> затвердили цей запит на злиття.
issue.action.reject=<b>@%[1]s</b> запитують зміни на цей запит на злиття.
@@ -1171,7 +1171,7 @@ issues.action_milestone_no_select=Етап відсутній
issues.action_assignee=Виконавець
issues.action_assignee_no_select=Немає виконавеця
issues.opened_by=%[1]s відкрито <a href="%[2]s">%[3]s</a>
pulls.merged_by=до <a href="%[2]s">%[3]</a> злито %[1]s
pulls.merged_by=до <a href="%[2]s">%[3]s</a> злито %[1]s
pulls.merged_by_fake=%[2]s об'єднаний %[1]s
issues.closed_by=закрито <a href="%[2]s">%[3]s</a> %[1]s
issues.opened_by_fake=%[2]s відкрив(ла) %[1]s
@@ -1285,7 +1285,7 @@ issues.error_modifying_due_date=Не вдалося змінити дату за
issues.error_removing_due_date=Не вдалося видалити дату завершення.
issues.push_commit_1=додав %d коміт %s
issues.push_commits_n=додав %d коміти(-ів) %s
issues.force_push_codes=`примусово залито %[1]s з <a class="ui sha" href="%[3]s"><code>%[2]</code></a> до <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.force_push_codes=`примусово залито %[1]s з <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> до <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.due_date_form=рррр-мм-дд
issues.due_date_form_add=Додати дату завершення
issues.due_date_form_edit=Редагувати
@@ -2221,7 +2221,7 @@ dashboard.clean_unbind_oauth_success=Всі незавершені зв'язки
dashboard.task.started=Запущено завдання: %[1]s
dashboard.task.process=Завдання: %[1]s
dashboard.task.cancelled=Завдання: %[1]s скасовано: %[3]s
dashboard.task.error=Помилка у завданні: %[1]:%[3]s
dashboard.task.error=Помилка у завданні: %[1]s :%[3]s
dashboard.task.finished=Завершилося завдання, яке запустив %[2]s: %[1]s
dashboard.task.unknown=Невідоме завдання: %[1]s
dashboard.cron.started=Запущено Cron: %[1]s
@@ -2701,7 +2701,7 @@ mirror_sync_delete=синхронізовано й видалено посила
approve_pull_request=`схвалив <a href="%s/pulls/%s">%s#%[2]s</a>`
reject_pull_request=`запропонував зміни до <a href="%s/pulls/%s">%s#%[2]s</a>`
publish_release=`опублікував випуск <a href="%s/releases/tag/%s"> "%[4]s" </a> з <a href="%[1]s">%[3]s</a>`
review_dismissed=`відхилений відгук від <b>%[4]</b> у <a href="%[1]s/pulls/%[2]s">%[3]s#%[2]s</a>`
review_dismissed=`відхилений відгук від <b>%[4]s</b> у <a href="%[1]s/pulls/%[2]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Причина:
create_branch=створено гілку <a href="%[1]s/src/branch/%[2]s">%[3]s</a> у <a href="%[1]s">%[4]s</a>

View File

@@ -1380,7 +1380,7 @@ pulls.filter_branch=过滤分支
pulls.no_results=未找到结果
pulls.nothing_to_compare=分支内容相同,无需创建合并请求。
pulls.nothing_to_compare_and_allow_empty_pr=这些分支是相等的,此合并请求将为空。
pulls.has_pull_request="在这些分支之间的合并请求已存在: <a href="%[1]s/pulls/%[3]d">%[2]s%#[3]d</a>"
pulls.has_pull_request="在这些分支之间的合并请求已存在: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>"
pulls.create=创建合并请求
pulls.title_desc=请求将 %[1]d 次代码提交从 <code>%[2]s</code> 合并至 <code id="branch_target">%[3]s</code>
pulls.merged_title_desc=于 %[4]s 将 %[1]d 次代码提交从 <code>%[2]s</code>合并至 <code>%[3]s</code>

View File

@@ -1 +1 @@
<svg viewBox="0 0 64 64" class="svg gitea-github" width="16" height="16" aria-hidden="true"><linearGradient id="a" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#a)" d="M25.008 56.007c-.003-.368-.006-1.962-.009-3.454l-.003-1.55c-6.729.915-8.358-3.78-8.376-3.83-.934-2.368-2.211-3.045-2.266-3.073l-.124-.072c-.463-.316-1.691-1.157-1.342-2.263.315-.997 1.536-1.1 2.091-1.082 3.074.215 4.63 2.978 4.694 3.095 1.569 2.689 3.964 2.411 5.509 1.844.144-.688.367-1.32.659-1.878-4.956-.879-10.571-3.515-10.571-13.104 0-2.633.82-4.96 2.441-6.929-.362-1.206-.774-3.666.446-6.765l.174-.442.452-.144c.416-.137 2.688-.624 7.359 2.433a24.959 24.959 0 0 1 6.074-.759c2.115.01 4.158.265 6.09.759 4.667-3.058 6.934-2.565 7.351-2.433l.451.145.174.44c1.225 3.098.813 5.559.451 6.766 1.618 1.963 2.438 4.291 2.438 6.929 0 9.591-5.621 12.219-10.588 13.087.563 1.065.868 2.402.868 3.878 0 1.683-.007 7.204-.015 8.402l-2-.014c.008-1.196.015-6.708.015-8.389 0-2.442-.943-3.522-1.35-3.874l-1.73-1.497 2.274-.253c5.205-.578 10.525-2.379 10.525-11.341 0-2.33-.777-4.361-2.31-6.036l-.43-.469.242-.587c.166-.401.894-2.442-.043-5.291-.758.045-2.568.402-5.584 2.447l-.384.259-.445-.123c-1.863-.518-3.938-.796-6.001-.806-2.052.01-4.124.288-5.984.806l-.445.123-.383-.259c-3.019-2.044-4.833-2.404-5.594-2.449-.935 2.851-.206 4.892-.04 5.293l.242.587-.429.469c-1.536 1.681-2.314 3.712-2.314 6.036 0 8.958 5.31 10.77 10.504 11.361l2.252.256-1.708 1.49c-.372.325-1.03 1.112-1.254 2.727l-.075.549-.506.227c-1.321.592-5.839 2.162-8.548-2.485-.015-.025-.544-.945-1.502-1.557.646.639 1.433 1.673 2.068 3.287.066.19 1.357 3.622 7.28 2.339l1.206-.262.012 3.978c.003 1.487.006 3.076.009 3.444l-1.998.014z"/><linearGradient id="b" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#b)" d="M32 58C17.663 58 6 46.337 6 32S17.663 6 32 6s26 11.663 26 26-11.663 26-26 26zm0-50C18.767 8 8 18.767 8 32s10.767 24 24 24 24-10.767 24-24S45.233 8 32 8z"/></svg>
<svg viewBox="0 0 64 64" class="svg gitea-github" width="16" height="16" aria-hidden="true"><linearGradient id="gitea-github__a" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#gitea-github__a)" d="M25.008 56.007c-.003-.368-.006-1.962-.009-3.454l-.003-1.55c-6.729.915-8.358-3.78-8.376-3.83-.934-2.368-2.211-3.045-2.266-3.073l-.124-.072c-.463-.316-1.691-1.157-1.342-2.263.315-.997 1.536-1.1 2.091-1.082 3.074.215 4.63 2.978 4.694 3.095 1.569 2.689 3.964 2.411 5.509 1.844.144-.688.367-1.32.659-1.878-4.956-.879-10.571-3.515-10.571-13.104 0-2.633.82-4.96 2.441-6.929-.362-1.206-.774-3.666.446-6.765l.174-.442.452-.144c.416-.137 2.688-.624 7.359 2.433a24.959 24.959 0 0 1 6.074-.759c2.115.01 4.158.265 6.09.759 4.667-3.058 6.934-2.565 7.351-2.433l.451.145.174.44c1.225 3.098.813 5.559.451 6.766 1.618 1.963 2.438 4.291 2.438 6.929 0 9.591-5.621 12.219-10.588 13.087.563 1.065.868 2.402.868 3.878 0 1.683-.007 7.204-.015 8.402l-2-.014c.008-1.196.015-6.708.015-8.389 0-2.442-.943-3.522-1.35-3.874l-1.73-1.497 2.274-.253c5.205-.578 10.525-2.379 10.525-11.341 0-2.33-.777-4.361-2.31-6.036l-.43-.469.242-.587c.166-.401.894-2.442-.043-5.291-.758.045-2.568.402-5.584 2.447l-.384.259-.445-.123c-1.863-.518-3.938-.796-6.001-.806-2.052.01-4.124.288-5.984.806l-.445.123-.383-.259c-3.019-2.044-4.833-2.404-5.594-2.449-.935 2.851-.206 4.892-.04 5.293l.242.587-.429.469c-1.536 1.681-2.314 3.712-2.314 6.036 0 8.958 5.31 10.77 10.504 11.361l2.252.256-1.708 1.49c-.372.325-1.03 1.112-1.254 2.727l-.075.549-.506.227c-1.321.592-5.839 2.162-8.548-2.485-.015-.025-.544-.945-1.502-1.557.646.639 1.433 1.673 2.068 3.287.066.19 1.357 3.622 7.28 2.339l1.206-.262.012 3.978c.003 1.487.006 3.076.009 3.444l-1.998.014z"/><linearGradient id="gitea-github__b" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#gitea-github__b)" d="M32 58C17.663 58 6 46.337 6 32S17.663 6 32 6s26 11.663 26 26-11.663 26-26 26zm0-50C18.767 8 8 18.767 8 32s10.767 24 24 24 24-10.767 24-24S45.233 8 32 8z"/></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -119,6 +119,7 @@ func GetArchive(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
repoPath := models.RepoPath(ctx.Params(":username"), ctx.Params(":reponame"))
if ctx.Repo.GitRepo == nil {
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
@@ -126,6 +127,7 @@ func GetArchive(ctx *context.APIContext) {
}
ctx.Repo.GitRepo = gitRepo
defer gitRepo.Close()
}
repo.Download(ctx.Context)
}

View File

@@ -752,6 +752,15 @@ func EditIssue(ctx *context.APIContext) {
}
}
if form.State != nil {
if issue.IsPull {
if pr, err := issue.GetPullRequest(); err != nil {
ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
return
} else if pr.HasMerged {
ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
return
}
}
issue.IsClosed = api.StateClosed == api.StateType(*form.State)
}
statusChangeComment, titleChanged, err := models.UpdateIssueByAPI(issue, ctx.User)

View File

@@ -115,7 +115,7 @@ func ListPullRequests(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
return
}
apiPrs[i] = convert.ToAPIPullRequest(prs[i])
apiPrs[i] = convert.ToAPIPullRequest(prs[i], ctx.User)
}
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
@@ -172,7 +172,7 @@ func GetPullRequest(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr))
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr, ctx.User))
}
// DownloadPullDiff render a pull's raw diff
@@ -437,7 +437,7 @@ func CreatePullRequest(ctx *context.APIContext) {
}
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr, ctx.User))
}
// EditPullRequest does what it says
@@ -584,6 +584,10 @@ func EditPullRequest(ctx *context.APIContext) {
}
if form.State != nil {
if pr.HasMerged {
ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
return
}
issue.IsClosed = api.StateClosed == api.StateType(*form.State)
}
statusChangeComment, titleChanged, err := models.UpdateIssueByAPI(issue, ctx.User)
@@ -605,7 +609,7 @@ func EditPullRequest(ctx *context.APIContext) {
}
// change pull target branch
if len(form.Base) != 0 && form.Base != pr.BaseBranch {
if !pr.HasMerged && len(form.Base) != 0 && form.Base != pr.BaseBranch {
if !ctx.Repo.GitRepo.IsBranchExist(form.Base) {
ctx.Error(http.StatusNotFound, "NewBaseBranchNotExist", fmt.Errorf("new base '%s' not exist", form.Base))
return
@@ -640,7 +644,7 @@ func EditPullRequest(ctx *context.APIContext) {
}
// TODO this should be 200, not 201
ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr, ctx.User))
}
// IsPullRequestMerged checks if a PR exists given an index
@@ -1130,6 +1134,9 @@ func UpdatePullRequest(ctx *context.APIContext) {
if models.IsErrMergeConflicts(err) {
ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict")
return
} else if models.IsErrRebaseConflicts(err) {
ctx.Error(http.StatusConflict, "Update", "rebase failed because of conflict")
return
}
ctx.Error(http.StatusInternalServerError, "pull_service.Update", err)
return

View File

@@ -374,16 +374,21 @@ func Generate(ctx *context.APIContext) {
ctxUser := ctx.User
var err error
if form.Owner != ctxUser.Name {
ctxUser, err = models.GetOrgByName(form.Owner)
ctxUser, err = models.GetUserByName(form.Owner)
if err != nil {
if models.IsErrOrgNotExist(err) {
if models.IsErrUserNotExist(err) {
ctx.JSON(http.StatusNotFound, map[string]interface{}{
"error": "request owner `" + form.Name + "` is not exist",
"error": "request owner `" + form.Owner + "` does not exist",
})
return
}
ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
return
}
if !ctx.User.IsAdmin && !ctxUser.IsOrganization() {
ctx.Error(http.StatusForbidden, "", "Only admin can generate repository for other user.")
return
}

View File

@@ -14,7 +14,6 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
@@ -147,15 +146,7 @@ func Recovery() func(next http.Handler) http.Handler {
"i18n": lc,
}
var user *models.User
if apiContext := context.GetAPIContext(req); apiContext != nil {
user = apiContext.User
}
if user == nil {
if ctx := context.GetContext(req); ctx != nil {
user = ctx.User
}
}
var user = context.GetContextUser(req)
if user == nil {
// Get user from session if logged in - do not attempt to sign-in
user = auth.SessionUser(sessionStore)

View File

@@ -752,6 +752,21 @@ func UpdatePullRequest(ctx *context.Context) {
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
return
} else if models.IsErrRebaseConflicts(err) {
conflictError := err.(models.ErrRebaseConflicts)
flashError, err := ctx.HTMLString(string(tplAlertDetails), map[string]interface{}{
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)),
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"),
"Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "<br>" + utils.SanitizeFlashErrorString(conflictError.StdOut),
})
if err != nil {
ctx.ServerError("UpdatePullRequest.HTMLString", err)
return
}
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
return
}
ctx.Flash.Error(err.Error())
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
@@ -1292,30 +1307,16 @@ func DownloadPullPatch(ctx *context.Context) {
// DownloadPullDiffOrPatch render a pull's raw diff or patch
func DownloadPullDiffOrPatch(ctx *context.Context, patch bool) {
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if models.IsErrIssueNotExist(err) {
ctx.NotFound("GetIssueByIndex", err)
if models.IsErrPullRequestNotExist(err) {
ctx.NotFound("GetPullRequestByIndex", err)
} else {
ctx.ServerError("GetIssueByIndex", err)
ctx.ServerError("GetPullRequestByIndex", err)
}
return
}
// Return not found if it's not a pull request
if !issue.IsPull {
ctx.NotFound("DownloadPullDiff",
fmt.Errorf("Issue is not a pull request"))
return
}
if err = issue.LoadPullRequest(); err != nil {
ctx.ServerError("LoadPullRequest", err)
return
}
pr := issue.PullRequest
if err := pull_service.DownloadDiffOrPatch(pr, ctx, patch); err != nil {
ctx.ServerError("DownloadDiffOrPatch", err)
return

View File

@@ -538,10 +538,8 @@ func SettingsPost(ctx *context.Context) {
return
}
repo.IsFork = false
repo.ForkID = 0
if err := models.UpdateRepository(repo, false); err != nil {
log.Error("Unable to update repository %-v whilst converting from fork", repo)
if err := repository.ConvertForkToNormalRepository(repo); err != nil {
log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err)
ctx.ServerError("Convert Fork", err)
return
}

View File

@@ -353,6 +353,10 @@ func renderDirectory(ctx *context.Context, treeLink string) {
}
} else {
ctx.Data["IsRenderedHTML"] = true
buf, err = ioutil.ReadAll(rd)
if err != nil {
log.Error("ReadAll failed: %v", err)
}
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(string(buf)), "\n", `<br>`,
)

View File

@@ -208,7 +208,7 @@ func DeleteKey(ctx *context.Context) {
return
}
if external {
ctx.Flash.Error(ctx.Tr("setting.ssh_externally_managed"))
ctx.Flash.Error(ctx.Tr("settings.ssh_externally_managed"))
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
return
}

View File

@@ -32,7 +32,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
t, err := models.GetTwoFactorByUID(ctx.User.ID)
if err != nil {
if models.IsErrTwoFactorNotEnrolled(err) {
ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
}
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
@@ -62,7 +62,7 @@ func DisableTwoFactor(ctx *context.Context) {
t, err := models.GetTwoFactorByUID(ctx.User.ID)
if err != nil {
if models.IsErrTwoFactorNotEnrolled(err) {
ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
}
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
@@ -150,7 +150,7 @@ func EnrollTwoFactor(ctx *context.Context) {
if t != nil {
// already enrolled - we should redirect back!
log.Warn("Trying to re-enroll %-v in twofa when already enrolled", ctx.User)
ctx.Flash.Error(ctx.Tr("setting.twofa_is_enrolled"))
ctx.Flash.Error(ctx.Tr("settings.twofa_is_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
}
@@ -175,7 +175,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
t, err := models.GetTwoFactorByUID(ctx.User.ID)
if t != nil {
// already enrolled
ctx.Flash.Error(ctx.Tr("setting.twofa_is_enrolled"))
ctx.Flash.Error(ctx.Tr("settings.twofa_is_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
}

View File

@@ -132,9 +132,11 @@ func doArchive(r *ArchiveRequest) (*models.RepoArchiver, error) {
if err == nil {
if archiver.Status == models.RepoArchiverGenerating {
archiver.Status = models.RepoArchiverReady
return archiver, models.UpdateRepoArchiverStatus(ctx, archiver)
if err = models.UpdateRepoArchiverStatus(ctx, archiver); err != nil {
return nil, err
}
return archiver, nil
}
return archiver, commiter.Commit()
}
if !errors.Is(err, os.ErrNotExist) {

View File

@@ -996,6 +996,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
// Create a new section to represent this hunk
curSection = &DiffSection{}
lastLeftIdx = -1
curFile.Sections = append(curFile.Sections, curSection)
lineSectionInfo := getDiffLineSectionInfo(curFile.Name, line, leftLine-1, rightLine-1)
@@ -1036,6 +1037,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
// Create a new section to represent this hunk
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
lastLeftIdx = -1
}
if lastLeftIdx > -1 {
diffLine.Match = lastLeftIdx
@@ -1061,6 +1063,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
// Create a new section to represent this hunk
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
lastLeftIdx = -1
}
if len(curSection.Lines) == 0 || curSection.Lines[len(curSection.Lines)-1].Type != DiffLineDel {
lastLeftIdx = len(curSection.Lines)
@@ -1121,6 +1124,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
curFile.IsBin = true
curFile.IsLFSFile = true
curSection.Lines = nil
lastLeftIdx = -1
}
}
}

View File

@@ -344,6 +344,16 @@ func sanitizeSubject(subject string) string {
// SendIssueAssignedMail composes and sends issue assigned email
func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, recipients []*models.User) error {
if setting.MailService == nil {
// No mail service configured
return nil
}
if err := issue.LoadRepo(); err != nil {
log.Error("Unable to load repo [%d] for issue #%d [%d]. Error: %v", issue.RepoID, issue.Index, issue.ID, err)
return err
}
langMap := make(map[string][]*models.User)
for _, user := range recipients {
langMap[user.Language] = append(langMap[user.Language], user)

View File

@@ -8,7 +8,7 @@
<title>{{.Subject}}</title>
</head>
{{$repo_url := printf "<a href='%s'>%s</a>" .Release.Repo.HTMLURL .Release.Repo.FullName}}
{{$repo_url := printf "<a href='%s'>%s</a>" .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
{{$link := printf "<a href='%s'>#%d</a>" .Link .Issue.Index}}
<body>
<p>

View File

@@ -62,7 +62,7 @@
{{end -}}
{{- range .ReviewComments}}
<hr>
{{.i18n.Tr "mail.issue.in_tree_path" .TreePath}}
{{$.i18n.Tr "mail.issue.in_tree_path" .TreePath}}
<div class="review">
<pre>{{.Patch}}</pre>
<div>{{.RenderedContent | Safe}}</div>

View File

@@ -2,7 +2,7 @@
<div class="page-content user signin{{if .LinkAccountMode}} icon{{end}}">
{{template "user/auth/signin_navbar" .}}
<div class="ui middle very relaxed page grid">
<div class="ui container column">
<div class="ui container column fluid">
{{template "user/auth/signin_inner" .}}
</div>
</div>

View File

@@ -1,4 +1,4 @@
<div class="ui container column{{if .LinkAccountMode}} icon{{end}}">
<div class="ui container column fluid{{if .LinkAccountMode}} icon{{end}}">
<h4 class="ui top attached header center">
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signup_title"}}

2
vendor/modules.txt vendored
View File

@@ -1047,7 +1047,7 @@ strk.kbt.io/projects/go/libravatar
# xorm.io/builder v0.3.9
## explicit
xorm.io/builder
# xorm.io/xorm v1.2.2
# xorm.io/xorm v1.2.5
## explicit
xorm.io/xorm
xorm.io/xorm/caches

View File

@@ -325,6 +325,9 @@ func AssignValue(dv reflect.Value, src interface{}) error {
if src == nil {
return nil
}
if v, ok := src.(*interface{}); ok {
return AssignValue(dv, *v)
}
if dv.Type().Implements(scannerType) {
return dv.Interface().(sql.Scanner).Scan(src)

View File

@@ -103,6 +103,39 @@ func (db *Base) URI() *URI {
return db.uri
}
// CreateTableSQL implements Dialect
func (db *Base) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
if tableName == "" {
tableName = table.Name
}
quoter := db.dialect.Quoter()
var b strings.Builder
b.WriteString("CREATE TABLE IF NOT EXISTS ")
quoter.QuoteTo(&b, tableName)
b.WriteString(" (")
for i, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1)
b.WriteString(s)
if i != len(table.ColumnsSeq())-1 {
b.WriteString(", ")
}
}
if len(table.PrimaryKeys) > 1 {
b.WriteString(", PRIMARY KEY (")
b.WriteString(quoter.Join(table.PrimaryKeys, ","))
b.WriteString(")")
}
b.WriteString(")")
return []string{b.String()}, false
}
// DropTableSQL returns drop table SQL
func (db *Base) DropTableSQL(tableName string) (string, bool) {
quote := db.dialect.Quoter().Quote
@@ -173,7 +206,7 @@ func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string {
// ModifyColumnSQL returns a SQL to modify SQL
func (db *Base) ModifyColumnSQL(tableName string, col *schemas.Column) string {
s, _ := ColumnString(db.dialect, col, false)
return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", tableName, s)
return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s)
}
// ForUpdateSQL returns for updateSQL

View File

@@ -423,7 +423,7 @@ func (db *mssql) DropTableSQL(tableName string) (string, bool) {
func (db *mssql) ModifyColumnSQL(tableName string, col *schemas.Column) string {
s, _ := ColumnString(db.dialect, col, false)
return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", tableName, s)
return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", db.quoter.Quote(tableName), s)
}
func (db *mssql) IndexCheckSQL(tableName, idxName string) (string, []interface{}) {
@@ -626,34 +626,37 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
}
func (db *mssql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
var sql string
if tableName == "" {
tableName = table.Name
}
sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE "
quoter := db.dialect.Quoter()
var b strings.Builder
b.WriteString("IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '")
quoter.QuoteTo(&b, tableName)
b.WriteString("' ) CREATE TABLE ")
quoter.QuoteTo(&b, tableName)
b.WriteString(" (")
sql += db.Quoter().Quote(tableName) + " ("
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
for i, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1)
sql += s
sql = strings.TrimSpace(sql)
sql += ", "
s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1)
b.WriteString(s)
if i != len(table.ColumnsSeq())-1 {
b.WriteString(", ")
}
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += strings.Join(pkList, ",")
sql += " ), "
if len(table.PrimaryKeys) > 1 {
b.WriteString(", PRIMARY KEY (")
b.WriteString(quoter.Join(table.PrimaryKeys, ","))
b.WriteString(")")
}
sql = sql[:len(sql)-2] + ")"
sql += ";"
return []string{sql}, true
b.WriteString(")")
return []string{b.String()}, true
}
func (db *mssql) ForUpdateSQL(query string) string {

View File

@@ -626,42 +626,43 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName
}
func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
var sql = "CREATE TABLE IF NOT EXISTS "
if tableName == "" {
tableName = table.Name
}
quoter := db.Quoter()
quoter := db.dialect.Quoter()
var b strings.Builder
b.WriteString("CREATE TABLE IF NOT EXISTS ")
quoter.QuoteTo(&b, tableName)
b.WriteString(" (")
sql += quoter.Quote(tableName)
sql += " ("
if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
for i, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1)
sql += s
sql = strings.TrimSpace(sql)
s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1)
b.WriteString(s)
if len(col.Comment) > 0 {
sql += " COMMENT '" + col.Comment + "'"
}
sql += ", "
b.WriteString(" COMMENT '")
b.WriteString(col.Comment)
b.WriteString("'")
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += quoter.Join(pkList, ",")
sql += " ), "
if i != len(table.ColumnsSeq())-1 {
b.WriteString(", ")
}
}
sql = sql[:len(sql)-2]
if len(table.PrimaryKeys) > 1 {
b.WriteString(", PRIMARY KEY (")
b.WriteString(quoter.Join(table.PrimaryKeys, ","))
b.WriteString(")")
}
sql += ")"
b.WriteString(")")
if table.StoreEngine != "" {
sql += " ENGINE=" + table.StoreEngine
b.WriteString(" ENGINE=")
b.WriteString(table.StoreEngine)
}
var charset = table.Charset
@@ -669,13 +670,15 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]strin
charset = db.URI().Charset
}
if len(charset) != 0 {
sql += " DEFAULT CHARSET " + charset
b.WriteString(" DEFAULT CHARSET ")
b.WriteString(charset)
}
if db.rowFormat != "" {
sql += " ROW_FORMAT=" + db.rowFormat
b.WriteString(" ROW_FORMAT=")
b.WriteString(db.rowFormat)
}
return []string{sql}, true
return []string{b.String()}, true
}
func (db *mysql) Filters() []Filter {

View File

@@ -965,41 +965,6 @@ func (db *postgres) AutoIncrStr() string {
return ""
}
func (db *postgres) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
var sql string
sql = "CREATE TABLE IF NOT EXISTS "
if tableName == "" {
tableName = table.Name
}
quoter := db.Quoter()
sql += quoter.Quote(tableName)
sql += " ("
if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1)
sql += s
sql = strings.TrimSpace(sql)
sql += ", "
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += quoter.Join(pkList, ",")
sql += " ), "
}
sql = sql[:len(sql)-2]
}
sql += ")"
return []string{sql}, true
}
func (db *postgres) IndexCheckSQL(tableName, idxName string) (string, []interface{}) {
if len(db.getSchema()) == 0 {
args := []interface{}{tableName, idxName}
@@ -1023,10 +988,10 @@ func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tabl
func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string {
if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") {
return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s",
tableName, col.Name, db.SQLType(col))
db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col))
}
return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s",
db.getSchema(), tableName, col.Name, db.SQLType(col))
db.quoter.Quote(db.getSchema()), db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col))
}
func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string {

View File

@@ -285,41 +285,6 @@ func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string {
return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName))
}
func (db *sqlite3) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
var sql string
sql = "CREATE TABLE IF NOT EXISTS "
if tableName == "" {
tableName = table.Name
}
quoter := db.Quoter()
sql += quoter.Quote(tableName)
sql += " ("
if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1)
sql += s
sql = strings.TrimSpace(sql)
sql += ", "
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += quoter.Join(pkList, ",")
sql += " ), "
}
sql = sql[:len(sql)-2]
}
sql += ")"
return []string{sql}, true
}
func (db *sqlite3) ForUpdateSQL(query string) string {
return query
}

25
vendor/xorm.io/xorm/internal/utils/new.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
// Copyright 2021 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package utils
import "reflect"
// New creates a value according type
func New(tp reflect.Type, length, cap int) reflect.Value {
switch tp.Kind() {
case reflect.Slice:
slice := reflect.MakeSlice(tp, length, cap)
x := reflect.New(slice.Type())
x.Elem().Set(slice)
return x
case reflect.Map:
mp := reflect.MakeMapWithSize(tp, cap)
x := reflect.New(mp.Type())
x.Elem().Set(mp)
return x
default:
return reflect.New(tp)
}
}

6
vendor/xorm.io/xorm/rows.go generated vendored
View File

@@ -84,12 +84,18 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
// Next move cursor to next record, return false if end has reached
func (rows *Rows) Next() bool {
if rows.rows != nil {
return rows.rows.Next()
}
return false
}
// Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close.
func (rows *Rows) Err() error {
if rows.rows != nil {
return rows.rows.Err()
}
return nil
}
// Scan row record to bean properties

5
vendor/xorm.io/xorm/session.go generated vendored
View File

@@ -174,6 +174,11 @@ func (session *Session) Engine() *Engine {
return session.engine
}
// Tx returns session tx
func (session *Session) Tx() *core.Tx {
return session.tx
}
func (session *Session) getQueryer() core.Queryer {
if session.tx != nil {
return session.tx

60
vendor/xorm.io/xorm/session_find.go generated vendored
View File

@@ -161,6 +161,16 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{})
}
func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect.Value, sqlStr string, args ...interface{}) error {
elemType := containerValue.Type().Elem()
var isPointer bool
if elemType.Kind() == reflect.Ptr {
isPointer = true
elemType = elemType.Elem()
}
if elemType.Kind() == reflect.Ptr {
return errors.New("pointer to pointer is not supported")
}
rows, err := session.queryRows(sqlStr, args...)
if err != nil {
return err
@@ -177,31 +187,8 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
return err
}
var newElemFunc func(fields []string) reflect.Value
elemType := containerValue.Type().Elem()
var isPointer bool
if elemType.Kind() == reflect.Ptr {
isPointer = true
elemType = elemType.Elem()
}
if elemType.Kind() == reflect.Ptr {
return errors.New("pointer to pointer is not supported")
}
newElemFunc = func(fields []string) reflect.Value {
switch elemType.Kind() {
case reflect.Slice:
slice := reflect.MakeSlice(elemType, len(fields), len(fields))
x := reflect.New(slice.Type())
x.Elem().Set(slice)
return x
case reflect.Map:
mp := reflect.MakeMap(elemType)
x := reflect.New(mp.Type())
x.Elem().Set(mp)
return x
}
return reflect.New(elemType)
var newElemFunc = func(fields []string) reflect.Value {
return utils.New(elemType, len(fields), len(fields))
}
var containerValueSetFunc func(*reflect.Value, schemas.PK) error
@@ -226,10 +213,15 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
containerValueSetFunc = func(newValue *reflect.Value, pk schemas.PK) error {
keyValue := reflect.New(keyType)
err := convertPKToValue(table, keyValue.Interface(), pk)
if err != nil {
cols := table.PKColumns()
if len(cols) == 1 {
if err := convert.AssignValue(keyValue, pk[0]); err != nil {
return err
}
} else {
keyValue.Set(reflect.ValueOf(&pk))
}
if isPointer {
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr())
} else {
@@ -241,8 +233,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
if elemType.Kind() == reflect.Struct {
var newValue = newElemFunc(fields)
dataStruct := utils.ReflectValue(newValue.Interface())
tb, err := session.engine.tagParser.ParseWithCache(dataStruct)
tb, err := session.engine.tagParser.ParseWithCache(newValue)
if err != nil {
return err
}
@@ -266,7 +257,6 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
default:
err = rows.Scan(bean)
}
if err != nil {
return err
}
@@ -278,16 +268,6 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
return rows.Err()
}
func convertPKToValue(table *schemas.Table, dst interface{}, pk schemas.PK) error {
cols := table.PKColumns()
if len(cols) == 1 {
return convert.Assign(dst, pk[0], nil, nil)
}
dst = pk
return nil
}
func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) {
if !session.canCache() ||
utils.IndexNoCase(sqlStr, "having") != -1 ||

View File

@@ -7,6 +7,9 @@ export default function initContextPopups() {
if (!refIssues.length) return;
refIssues.each(function () {
if ($(this).hasClass('ref-external-issue')) {
return;
}
const [index, _issues, repo, owner] = $(this).attr('href').replace(/[#?].*$/, '').split('/').reverse();
const el = document.createElement('div');

View File

@@ -181,6 +181,7 @@ details summary > * {
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
box-shadow: inset 0 0 0 6px var(--color-primary);
@@ -1285,6 +1286,7 @@ footer {
display: flex;
margin-left: auto;
margin-right: auto;
overflow-x: auto;
}
@media @mediaSm {