Compare commits

..

26 Commits

Author SHA1 Message Date
John Olheiser
aa1d9ef6cb Changelog 1.12.5 (#13002)
* Changelog 1.12.5

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Update CHANGELOG.md

* Update CHANGELOG.md

Co-authored-by: techknowlogick <matti@mdranta.net>

* Apply suggestions from code review

Co-authored-by: techknowlogick <matti@mdranta.net>

Co-authored-by: techknowlogick <matti@mdranta.net>
2020-10-01 13:35:22 -05:00
techknowlogick
20a75f86a1 allow U2F with default settings for gitea in subpath (#12990) (#13001)
* allow U2F with default settings for gitea in subpath

* use trim suffix

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

Co-authored-by: zeripath <art27@cantab.net>
2020-10-01 13:22:49 -04:00
techknowlogick
c1c5e00d20 Prevent empty div when editing comment (#12404) (#12991)
* Prevent empty div when editing comment

The template for attachments needs to remove whitespace and return empty when there are no attachments.

Fix #10220

Co-authored-by: zeripath <art27@cantab.net>
2020-10-01 09:25:57 +08:00
6543
3e279dfb0b Mirror: Update DB on Address-Update too (#12964) (#12967)
* Mirror: Update DB on Address-Update too (#12964)

* Mirror: Update DB on Address-Update too

* new name for function to better describe

* fix lint
2020-09-28 21:52:13 +02:00
zeripath
e9346fc4a9 Allow extended config on cron settings (#12939) (#12943)
Backport #12939

Fix #12934

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-09-25 10:19:20 -05:00
zeripath
b62e13a001 Open transaction when adding Avatar email-hash pairs to the DB (#12577) (#12940)
Backport #12577

When adding Avatar email-hash pairs we simply want the DB table to
represent a Set. We don't care if the hash-pair is already present,
so we just simply Insert and ignore the error.

Unfortunately this seems to cause some DBs to log the duplicate
insert to their logs - looking like a bug a in Gitea.

Now, there is no standard way in SQL to say Insert but if there's
an error ignore it. MySQL has INSERT IGNORE, PostgreSQL >= 9.5 has
INSERT ... ON CONFLICT DO NOTHING, but I do not believe that SQLite
or MSSQL have variants.

This PR places the insert in a transaction which we are happy to fail
if there is an error - hopefully this will stop the unnecessary
logging.

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

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

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-09-24 18:36:26 -04:00
赵智超
470b195da1 Fix ListUserOrgs (#12910) (#12915)
fix #12891

Signed-off-by: a1012112796 <1012112796@qq.com>

Co-authored-by: Lauris BH <lauris@nix.lv>

Co-authored-by: Lauris BH <lauris@nix.lv>
2020-09-21 01:04:33 -04:00
zeripath
09178300b0 Update only the repository columns that need updating (#12900) (#12912)
Backport #12900

We should only update is_empty, default_branch and updated time columns
during commitRepoAction and not update other columns as we risk
overwriting incorrect information.

Fix #11823
Fix #10536

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-09-21 09:06:23 +08:00
mrsdizzie
23aae3274a Fix panic when adding long comment (#12892) (#12894)
Previous PR #12881 causes out of bounds panic by working on wrong string.
2020-09-18 16:45:00 -04:00
6543
a98bf03204 Convert User expose ID each time (#12855) (#12883)
* Convert User expose ID each time (#12855)

* git blame tells me a lot of gitea things happen here around 2018, add header

* move user code int its own file

* expose user id

* adopt things from APIFormat

* fix test

* CI.restart()

* No Refactor

* CI.restart()

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-09-18 12:56:16 -04:00
赵智超
65aef7b35f Add size limit for content of comment on action ui (#12881) (#12890)
Signed-off-by: a1012112796 <1012112796@qq.com>
Co-authored-by: mrsdizzie <info@mrsdizzie.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

Co-authored-by: mrsdizzie <info@mrsdizzie.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-09-18 19:58:49 +08:00
Lunny Xiao
65ef634d5c Fix pgsql migration test (#12844) (#12884)
Co-authored-by: Lauris BH <lauris@nix.lv>

Co-authored-by: Lauris BH <lauris@nix.lv>
2020-09-18 16:28:13 +08:00
6543
869fd17b88 Fix [API] CreateRepo missing information (#12848) (#12867)
* Fix [API] [Bug] CreateRepo missing information (#12848)

* Fix [API] [Bug] CreateRepo missing information

* add code comment

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

* CI.restart()

* CI.restart()

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
2020-09-18 13:58:04 +08:00
zeripath
d624e91c0c Support slashes in release tags (#12864) (#12882)
Backport #12864

Fix #12861

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-09-17 15:33:48 -05:00
Lunny Xiao
62a3c847cd Fix migration v111 (#12868) 2020-09-16 10:37:04 +03:00
zeripath
3a02f0896e Escape more things that are passed through str2html (#12622) (#12850)
Backport #12622

* Escape more things that are passed through str2html

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

* Bloody editors!

Co-authored-by: mrsdizzie <info@mrsdizzie.com>

* Update routers/user/oauth.go
2020-09-15 18:43:10 -04:00
zeripath
408db95dc1 Fix notifications page links (#12838) (#12853)
Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-09-15 17:53:30 +01:00
6543
6305f07fdc On Migration respect old DefaultBranch (#12843) (#12858)
* On Migration respect old DefaultBranch

* add DefaultBranch int test set

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

Co-authored-by: zeripath <art27@cantab.net>
2020-09-15 12:12:07 -04:00
zeripath
ff9d99f63d Stop cloning unnecessarily on PR update (#12839) (#12852)
Backport #12839

Fix #12740

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-09-15 13:09:25 +03:00
techknowlogick
37572551d7 Remove double escape on labels addition in comments (#12809) (#12810)
Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-09-11 00:56:12 -04:00
赵智超
0ee823be0b Fix "only mail on mention" bug (#12775) (#12789)
* fix mail mention bug

fix #12774

Signed-off-by: a1012112796 <1012112796@qq.com>

* fix test

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

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-09-10 11:32:54 +03:00
zeripath
062ea40a79 Fix yet another bug with diff file names (#12771) (#12776)
Backport #12771

Following further testing it has become apparent that the diff line
cannot be used to determine filenames for diffs with any sort of predictability
the answer therefore is to use the other lines that are provided with a diff

Fix #12768

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-09-09 19:03:54 +01:00
6543
7a25441abe gitea dump: include version & Check InstallLock (#12760) (#12762)
* gitea dump: include version

* Check InstallLock
2020-09-07 20:44:45 -04:00
6543
dc71d00393 RepoInit Respect AlternateDefaultBranch (#12746) (#12751) 2020-09-06 18:03:50 -04:00
6543
0bb56a413d Fix Avatar Resize (resize algo NearestNeighbor -> Bilinear) (#12745) (#12750)
* Update Vendor github.com/nfnt/resize

* switch resize algo NearestNeighbor -> Bilinear
2020-09-06 22:14:59 +01:00
6543
2806a312e1 [Backport] Fix go1.15 lint error in modules/public/public.go (#12707) (#12708)
* fix go1.15 lint error in modules/public/public.go

* CI.restart()
2020-09-04 08:28:08 +03:00
56 changed files with 906 additions and 344 deletions

View File

@@ -4,6 +4,33 @@ 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 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). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.12.5](https://github.com/go-gitea/gitea/releases/tag/v1.12.5) - 2020-10-01
* BUGFIXES
* Allow U2F with default settings for gitea in subpath (#12990) (#13001)
* Prevent empty div when editing comment (#12404) (#12991)
* On mirror update also update address in DB (#12964) (#12967)
* Allow extended config on cron settings (#12939) (#12943)
* Open transaction when adding Avatar email-hash pairs to the DB (#12577) (#12940)
* Fix internal server error from ListUserOrgs API (#12910) (#12915)
* Update only the repository columns that need updating (#12900) (#12912)
* Fix panic when adding long comment (#12892) (#12894)
* Add size limit for content of comment on action ui (#12881) (#12890)
* Convert User expose ID each time (#12855) (#12883)
* Support slashes in release tags (#12864) (#12882)
* Add missing information to CreateRepo API endpoint (#12848) (#12867)
* On Migration respect old DefaultBranch (#12843) (#12858)
* Fix notifications page links (#12838) (#12853)
* Stop cloning unnecessarily on PR update (#12839) (#12852)
* Escape more things that are passed through str2html (#12622) (#12850)
* Remove double escape on labels addition in comments (#12809) (#12810)
* Fix "only mail on mention" bug (#12775) (#12789)
* Fix yet another bug with diff file names (#12771) (#12776)
* RepoInit Respect AlternateDefaultBranch (#12746) (#12751)
* Fix Avatar Resize (resize algo NearestNeighbor -> Bilinear) (#12745) (#12750)
* ENHANCEMENTS
* gitea dump: include version & Check InstallLock (#12760) (#12762)
## [1.12.4](https://github.com/go-gitea/gitea/releases/tag/v1.12.4) - 2020-09-02 ## [1.12.4](https://github.com/go-gitea/gitea/releases/tag/v1.12.4) - 2020-09-02
* SECURITY * SECURITY

View File

@@ -66,6 +66,10 @@ func fatal(format string, args ...interface{}) {
func runDump(ctx *cli.Context) error { func runDump(ctx *cli.Context) error {
setting.NewContext() setting.NewContext()
if !setting.InstallLock {
log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
return fmt.Errorf("gitea is not initialized")
}
setting.NewServices() // cannot access session settings otherwise setting.NewServices() // cannot access session settings otherwise
err := models.SetEngine() err := models.SetEngine()

2
go.mod
View File

@@ -75,7 +75,7 @@ require (
github.com/microcosm-cc/bluemonday v1.0.3-0.20191119130333-0a75d7616912 github.com/microcosm-cc/bluemonday v1.0.3-0.20191119130333-0a75d7616912
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/niklasfasching/go-org v0.1.9 github.com/niklasfasching/go-org v0.1.9
github.com/oliamb/cutter v0.2.2 github.com/oliamb/cutter v0.2.2
github.com/olivere/elastic/v7 v7.0.9 github.com/olivere/elastic/v7 v7.0.9

4
go.sum
View File

@@ -483,8 +483,8 @@ github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOl
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc h1:z1PgdCCmYYVL0BoJTUgmAq1p7ca8fzYIPsNyfsN3xAU= github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc h1:z1PgdCCmYYVL0BoJTUgmAq1p7ca8fzYIPsNyfsN3xAU=
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs= github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/niklasfasching/go-org v0.1.9 h1:Toz8WMIt+qJb52uYEk1YD/muLuOOmRt1CfkV+bKVMkI= github.com/niklasfasching/go-org v0.1.9 h1:Toz8WMIt+qJb52uYEk1YD/muLuOOmRt1CfkV+bKVMkI=

View File

@@ -43,7 +43,7 @@ func TestAPIPullReview(t *testing.T) {
assert.EqualValues(t, 10, reviews[5].ID) assert.EqualValues(t, 10, reviews[5].ID)
assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State) assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State)
assert.EqualValues(t, 1, reviews[5].CodeCommentsCount) assert.EqualValues(t, 1, reviews[5].CodeCommentsCount)
assert.EqualValues(t, 0, reviews[5].Reviewer.ID) // ghost user assert.EqualValues(t, -1, reviews[5].Reviewer.ID) // ghost user
assert.EqualValues(t, false, reviews[5].Stale) assert.EqualValues(t, false, reviews[5].Stale)
assert.EqualValues(t, true, reviews[5].Official) assert.EqualValues(t, true, reviews[5].Official)

View File

@@ -5,9 +5,12 @@
package integrations package integrations
import ( import (
"fmt"
"net/http" "net/http"
"testing" "testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -45,8 +48,14 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) {
var results SearchResults var results SearchResults
DecodeJSON(t, resp, &results) DecodeJSON(t, resp, &results)
assert.NotEmpty(t, results.Data) assert.NotEmpty(t, results.Data)
var modelUser *models.User
for _, user := range results.Data { for _, user := range results.Data {
assert.Contains(t, user.UserName, query) assert.Contains(t, user.UserName, query)
assert.Empty(t, user.Email) modelUser = models.AssertExistsAndLoadBean(t, &models.User{ID: user.ID}).(*models.User)
if modelUser.KeepEmailPrivate {
assert.EqualValues(t, fmt.Sprintf("%s@%s", modelUser.LowerName, setting.Service.NoReplyAddress), user.Email)
} else {
assert.EqualValues(t, modelUser.Email, user.Email)
}
} }
} }

View File

@@ -152,6 +152,7 @@ func restoreOldDB(t *testing.T, version string) bool {
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)) _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name))
assert.NoError(t, err) assert.NoError(t, err)
db.Close()
db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?multiStatements=true", db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?multiStatements=true",
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name)) setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name))
@@ -182,6 +183,8 @@ func restoreOldDB(t *testing.T, version string) bool {
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
return false return false
} }
defer db.Close()
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
if !assert.NoError(t, err) || !assert.NotEmpty(t, schrows) { if !assert.NoError(t, err) || !assert.NotEmpty(t, schrows) {
return false return false

View File

@@ -41,7 +41,18 @@ func AvatarLink(email string) string {
Email: lowerEmail, Email: lowerEmail,
Hash: sum, Hash: sum,
} }
_, _ = x.Insert(emailHash) // OK we're going to open a session just because I think that that might hide away any problems with postgres reporting errors
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
// we don't care about any DB problem just return the lowerEmail
return lowerEmail, nil
}
_, _ = sess.Insert(emailHash)
if err := sess.Commit(); err != nil {
// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time
return lowerEmail, nil
}
return lowerEmail, nil return lowerEmail, nil
}) })
return setting.AppSubURL + "/avatar/" + url.PathEscape(sum) return setting.AppSubURL + "/avatar/" + url.PathEscape(sum)

View File

@@ -11,7 +11,6 @@ import (
) )
func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
type ProtectedBranch struct { type ProtectedBranch struct {
CanPush bool `xorm:"NOT NULL DEFAULT false"` CanPush bool `xorm:"NOT NULL DEFAULT false"`
EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"` EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
@@ -23,29 +22,26 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
Official bool `xorm:"NOT NULL DEFAULT false"` Official bool `xorm:"NOT NULL DEFAULT false"`
} }
sess := x.NewSession() if err := x.Sync2(new(ProtectedBranch)); err != nil {
defer sess.Close()
if err := sess.Sync2(new(ProtectedBranch)); err != nil {
return err return err
} }
if err := sess.Sync2(new(Review)); err != nil { if err := x.Sync2(new(Review)); err != nil {
return err return err
} }
if _, err := sess.Exec("UPDATE `protected_branch` SET `enable_whitelist` = ? WHERE enable_whitelist IS NULL", false); err != nil { if _, err := x.Exec("UPDATE `protected_branch` SET `enable_whitelist` = ? WHERE enable_whitelist IS NULL", false); err != nil {
return err return err
} }
if _, err := sess.Exec("UPDATE `protected_branch` SET `can_push` = `enable_whitelist`"); err != nil { if _, err := x.Exec("UPDATE `protected_branch` SET `can_push` = `enable_whitelist`"); err != nil {
return err return err
} }
if _, err := sess.Exec("UPDATE `protected_branch` SET `enable_approvals_whitelist` = ? WHERE `required_approvals` > ?", true, 0); err != nil { if _, err := x.Exec("UPDATE `protected_branch` SET `enable_approvals_whitelist` = ? WHERE `required_approvals` > ?", true, 0); err != nil {
return err return err
} }
var pageSize int64 = 20 var pageSize int64 = 20
qresult, err := sess.QueryInterface("SELECT max(id) as max_id FROM issue") qresult, err := x.QueryInterface("SELECT max(id) as max_id FROM issue")
if err != nil { if err != nil {
return err return err
} }
@@ -57,10 +53,19 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
} }
totalPages := totalIssues / pageSize totalPages := totalIssues / pageSize
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
// Find latest review of each user in each pull request, and set official field if appropriate // Find latest review of each user in each pull request, and set official field if appropriate
reviews := []*models.Review{}
var page int64 var page int64
var count int
for page = 0; page <= totalPages; page++ { for page = 0; page <= totalPages; page++ {
reviews := []*models.Review{}
if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id > ? AND issue_id <= ? AND type in (?, ?) GROUP BY issue_id, reviewer_id)", if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id > ? AND issue_id <= ? AND type in (?, ?) GROUP BY issue_id, reviewer_id)",
page*pageSize, (page+1)*pageSize, models.ReviewTypeApprove, models.ReviewTypeReject). page*pageSize, (page+1)*pageSize, models.ReviewTypeApprove, models.ReviewTypeReject).
Find(&reviews); err != nil { Find(&reviews); err != nil {
@@ -68,23 +73,37 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
} }
for _, review := range reviews { for _, review := range reviews {
if err := review.LoadAttributes(); err != nil { if err := review.LoadAttributesX(sess); err != nil {
// Error might occur if user or issue doesn't exist, ignore it. // Error might occur if user or issue doesn't exist, ignore it.
continue continue
} }
official, err := models.IsOfficialReviewer(review.Issue, review.Reviewer) official, err := models.IsOfficialReviewerX(sess, review.Issue, review.Reviewer)
if err != nil { if err != nil {
// Branch might not be proteced or other error, ignore it. // Branch might not be proteced or other error, ignore it.
continue continue
} }
review.Official = official review.Official = official
count++
if _, err := sess.ID(review.ID).Cols("official").Update(review); err != nil { if _, err := sess.ID(review.ID).Cols("official").Update(review); err != nil {
return err return err
} }
if count == 100 {
if err := sess.Commit(); err != nil {
return err
}
count = 0
if err := sess.Begin(); err != nil {
return err
}
}
}
} }
} if count > 0 {
return sess.Commit() return sess.Commit()
}
return nil
} }

View File

@@ -264,6 +264,17 @@ func DumpDatabase(filePath string, dbType string) error {
} }
tbs = append(tbs, t) tbs = append(tbs, t)
} }
type Version struct {
ID int64 `xorm:"pk autoincr"`
Version int64
}
t, err := x.TableInfo(Version{})
if err != nil {
return err
}
tbs = append(tbs, t)
if len(dbType) > 0 { if len(dbType) > 0 {
return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType)) return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
} }

View File

@@ -21,6 +21,12 @@ func TestDumpDatabase(t *testing.T) {
dir, err := ioutil.TempDir(os.TempDir(), "dump") dir, err := ioutil.TempDir(os.TempDir(), "dump")
assert.NoError(t, err) assert.NoError(t, err)
type Version struct {
ID int64 `xorm:"pk autoincr"`
Version int64
}
assert.NoError(t, x.Sync2(Version{}))
for _, dbName := range setting.SupportedDatabases { for _, dbName := range setting.SupportedDatabases {
dbType := setting.GetDBTypeByName(dbName) dbType := setting.GetDBTypeByName(dbName)
assert.NoError(t, DumpDatabase(filepath.Join(dir, dbType+".sql"), dbType)) assert.NoError(t, DumpDatabase(filepath.Join(dir, dbType+".sql"), dbType))

View File

@@ -110,7 +110,8 @@ func (r *Review) LoadReviewer() error {
return r.loadReviewer(x) return r.loadReviewer(x)
} }
func (r *Review) loadAttributes(e Engine) (err error) { // LoadAttributesX loads all attributes except CodeComments with an Engine parameter
func (r *Review) LoadAttributesX(e Engine) (err error) {
if err = r.loadIssue(e); err != nil { if err = r.loadIssue(e); err != nil {
return return
} }
@@ -125,7 +126,7 @@ func (r *Review) loadAttributes(e Engine) (err error) {
// LoadAttributes loads all attributes except CodeComments // LoadAttributes loads all attributes except CodeComments
func (r *Review) LoadAttributes() error { func (r *Review) LoadAttributes() error {
return r.loadAttributes(x) return r.LoadAttributesX(x)
} }
func getReviewByID(e Engine, id int64) (*Review, error) { func getReviewByID(e Engine, id int64) (*Review, error) {
@@ -203,6 +204,12 @@ func IsOfficialReviewer(issue *Issue, reviewer *User) (bool, error) {
return isOfficialReviewer(x, issue, reviewer) return isOfficialReviewer(x, issue, reviewer)
} }
// IsOfficialReviewerX check if reviewer can make official reviews in issue (counts towards required approvals)
// with an Engine parameter
func IsOfficialReviewerX(e Engine, issue *Issue, reviewer *User) (bool, error) {
return isOfficialReviewer(x, issue, reviewer)
}
func isOfficialReviewer(e Engine, issue *Issue, reviewer *User) (bool, error) { func isOfficialReviewer(e Engine, issue *Issue, reviewer *User) (bool, error) {
pr, err := getPullRequestByIssueID(e, issue.ID) pr, err := getPullRequestByIssueID(e, issue.ID)
if err != nil { if err != nil {

View File

@@ -1418,11 +1418,21 @@ func getUserEmailsByNames(e Engine, names []string) []string {
} }
// GetMaileableUsersByIDs gets users from ids, but only if they can receive mails // GetMaileableUsersByIDs gets users from ids, but only if they can receive mails
func GetMaileableUsersByIDs(ids []int64) ([]*User, error) { func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) {
if len(ids) == 0 { if len(ids) == 0 {
return nil, nil return nil, nil
} }
ous := make([]*User, 0, len(ids)) ous := make([]*User, 0, len(ids))
if isMention {
return ous, x.In("id", ids).
Where("`type` = ?", UserTypeIndividual).
And("`prohibit_login` = ?", false).
And("`is_active` = ?", true).
And("`email_notifications_preference` IN ( ?, ?)", EmailNotificationsEnabled, EmailNotificationsOnMention).
Find(&ous)
}
return ous, x.In("id", ids). return ous, x.In("id", ids).
Where("`type` = ?", UserTypeIndividual). Where("`type` = ?", UserTypeIndividual).
And("`prohibit_login` = ?", false). And("`prohibit_login` = ?", false).

View File

@@ -389,3 +389,20 @@ func TestGetUserIDsByNames(t *testing.T) {
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, []int64(nil), IDs) assert.Equal(t, []int64(nil), IDs)
} }
func TestGetMaileableUsersByIDs(t *testing.T) {
results, err := GetMaileableUsersByIDs([]int64{1, 4}, false)
assert.NoError(t, err)
assert.Equal(t, 1, len(results))
if len(results) > 1 {
assert.Equal(t, results[0].ID, 1)
}
results, err = GetMaileableUsersByIDs([]int64{1, 4}, true)
assert.NoError(t, err)
assert.Equal(t, 2, len(results))
if len(results) > 2 {
assert.Equal(t, results[0].ID, 1)
assert.Equal(t, results[1].ID, 4)
}
}

View File

@@ -89,6 +89,6 @@ func Prepare(data []byte) (*image.Image, error) {
} }
} }
img = resize.Resize(AvatarSize, AvatarSize, img, resize.NearestNeighbor) img = resize.Resize(AvatarSize, AvatarSize, img, resize.Bilinear)
return &img, nil return &img, nil
} }

View File

@@ -1,4 +1,5 @@
// Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@@ -335,9 +336,11 @@ func ToTeam(team *models.Team) *api.Team {
// signed shall only be set if requester is logged in. authed shall only be set if user is site admin or user himself // signed shall only be set if requester is logged in. authed shall only be set if user is site admin or user himself
func ToUser(user *models.User, signed, authed bool) *api.User { func ToUser(user *models.User, signed, authed bool) *api.User {
result := &api.User{ result := &api.User{
ID: user.ID,
UserName: user.Name, UserName: user.Name,
AvatarURL: user.AvatarLink(),
FullName: markup.Sanitize(user.FullName), FullName: markup.Sanitize(user.FullName),
Email: user.GetEmail(),
AvatarURL: user.AvatarLink(),
Created: user.CreatedUnix.AsTime(), Created: user.CreatedUnix.AsTime(),
} }
// hide primary email if API caller is anonymous or user keep email private // hide primary email if API caller is anonymous or user keep email private
@@ -346,7 +349,6 @@ func ToUser(user *models.User, signed, authed bool) *api.User {
} }
// only site admin will get these information and possibly user himself // only site admin will get these information and possibly user himself
if authed { if authed {
result.ID = user.ID
result.IsAdmin = user.IsAdmin result.IsAdmin = user.IsAdmin
result.LastLogin = user.LastLoginUnix.AsTime() result.LastLogin = user.LastLoginUnix.AsTime()
result.Language = user.Language result.Language = user.Language

View File

@@ -16,4 +16,5 @@ type Repository struct {
AuthPassword string AuthPassword string
CloneURL string CloneURL string
OriginalURL string OriginalURL string
DefaultBranch string
} }

View File

@@ -119,6 +119,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
if err != nil { if err != nil {
return err return err
} }
r.DefaultBranch = repo.DefaultBranch
r, err = repository.MigrateRepositoryGitData(g.doer, owner, r, structs.MigrateRepoOption{ r, err = repository.MigrateRepositoryGitData(g.doer, owner, r, structs.MigrateRepoOption{
RepoName: g.repoName, RepoName: g.repoName,

View File

@@ -154,6 +154,11 @@ func (g *GithubDownloaderV3) GetRepoInfo() (*base.Repository, error) {
} }
g.rate = &resp.Rate g.rate = &resp.Rate
defaultBranch := ""
if gr.DefaultBranch != nil {
defaultBranch = *gr.DefaultBranch
}
// convert github repo to stand Repo // convert github repo to stand Repo
return &base.Repository{ return &base.Repository{
Owner: g.repoOwner, Owner: g.repoOwner,
@@ -162,6 +167,7 @@ func (g *GithubDownloaderV3) GetRepoInfo() (*base.Repository, error) {
Description: gr.GetDescription(), Description: gr.GetDescription(),
OriginalURL: gr.GetHTMLURL(), OriginalURL: gr.GetHTMLURL(),
CloneURL: gr.GetCloneURL(), CloneURL: gr.GetCloneURL(),
DefaultBranch: defaultBranch,
}, nil }, nil
} }

View File

@@ -76,6 +76,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
Description: "Test repository for testing migration from github to gitea", Description: "Test repository for testing migration from github to gitea",
CloneURL: "https://github.com/go-gitea/test_repo.git", CloneURL: "https://github.com/go-gitea/test_repo.git",
OriginalURL: "https://github.com/go-gitea/test_repo", OriginalURL: "https://github.com/go-gitea/test_repo",
DefaultBranch: "master",
}, repo) }, repo)
topics, err := downloader.GetTopics() topics, err := downloader.GetTopics()

View File

@@ -163,6 +163,7 @@ func (g *GitlabDownloader) GetRepoInfo() (*base.Repository, error) {
Description: gr.Description, Description: gr.Description,
OriginalURL: gr.WebURL, OriginalURL: gr.WebURL,
CloneURL: gr.HTTPURLToRepo, CloneURL: gr.HTTPURLToRepo,
DefaultBranch: gr.DefaultBranch,
}, nil }, nil
} }

View File

@@ -40,6 +40,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
Description: "Test repository for testing migration from gitlab to gitea", Description: "Test repository for testing migration from gitlab to gitea",
CloneURL: "https://gitlab.com/gitea/test_repo.git", CloneURL: "https://gitlab.com/gitea/test_repo.git",
OriginalURL: "https://gitlab.com/gitea/test_repo", OriginalURL: "https://gitlab.com/gitea/test_repo",
DefaultBranch: "master",
}, repo) }, repo)
topics, err := downloader.GetTopics() topics, err := downloader.GetTopics()

View File

@@ -92,13 +92,22 @@ func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *model
act := &models.Action{ act := &models.Action{
ActUserID: doer.ID, ActUserID: doer.ID,
ActUser: doer, ActUser: doer,
Content: fmt.Sprintf("%d|%s", issue.Index, comment.Content),
RepoID: issue.Repo.ID, RepoID: issue.Repo.ID,
Repo: issue.Repo, Repo: issue.Repo,
Comment: comment, Comment: comment,
CommentID: comment.ID, CommentID: comment.ID,
IsPrivate: issue.Repo.IsPrivate, IsPrivate: issue.Repo.IsPrivate,
} }
content := ""
if len(comment.Content) > 200 {
content = comment.Content[:strings.LastIndex(comment.Content[0:200], " ")] + "…"
} else {
content = comment.Content
}
act.Content = fmt.Sprintf("%d|%s", issue.Index, content)
if issue.IsPull { if issue.IsPull {
act.OpType = models.ActionCommentPull act.OpType = models.ActionCommentPull
} else { } else {

View File

@@ -6,6 +6,7 @@ package public
import ( import (
"encoding/base64" "encoding/base64"
"fmt"
"log" "log"
"net/http" "net/http"
"path" "path"
@@ -159,7 +160,7 @@ func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options)
// Add an Expires header to the static content // Add an Expires header to the static content
if opt.ExpiresAfter > 0 { if opt.ExpiresAfter > 0 {
ctx.Resp.Header().Set("Expires", time.Now().Add(opt.ExpiresAfter).UTC().Format(http.TimeFormat)) ctx.Resp.Header().Set("Expires", time.Now().Add(opt.ExpiresAfter).UTC().Format(http.TimeFormat))
tag := GenerateETag(string(fi.Size()), fi.Name(), fi.ModTime().UTC().Format(http.TimeFormat)) tag := GenerateETag(fmt.Sprint(fi.Size()), fi.Name(), fi.ModTime().UTC().Format(http.TimeFormat))
ctx.Resp.Header().Set("ETag", tag) ctx.Resp.Header().Set("ETag", tag)
if ctx.Req.Header.Get("If-None-Match") == tag { if ctx.Req.Header.Get("If-None-Match") == tag {
ctx.Resp.WriteHeader(304) ctx.Resp.WriteHeader(304)

View File

@@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"html" "html"
"time"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
@@ -176,7 +177,7 @@ func CommitRepoAction(optsList ...*CommitRepoActionOptions) error {
var err error var err error
if repo != nil { if repo != nil {
// Change repository empty status and update last updated time. // Change repository empty status and update last updated time.
if err := models.UpdateRepository(repo, false); err != nil { if err := models.UpdateRepositoryUpdatedTime(repo.ID, time.Now()); err != nil {
return fmt.Errorf("UpdateRepository: %v", err) return fmt.Errorf("UpdateRepository: %v", err)
} }
} }
@@ -204,6 +205,10 @@ func CommitRepoAction(optsList ...*CommitRepoActionOptions) error {
} }
gitRepo.Close() gitRepo.Close()
} }
// Update the is empty and default_branch columns
if err := models.UpdateRepositoryCols(repo, "default_branch", "is_empty"); err != nil {
return fmt.Errorf("UpdateRepositoryCols: %v", err)
}
} }
opType := models.ActionCommitRepo opType := models.ActionCommitRepo
@@ -274,7 +279,7 @@ func CommitRepoAction(optsList ...*CommitRepoActionOptions) error {
if repo != nil { if repo != nil {
// Change repository empty status and update last updated time. // Change repository empty status and update last updated time.
if err := models.UpdateRepository(repo, false); err != nil { if err := models.UpdateRepositoryUpdatedTime(repo.ID, time.Now()); err != nil {
return fmt.Errorf("UpdateRepository: %v", err) return fmt.Errorf("UpdateRepository: %v", err)
} }
} }

View File

@@ -23,6 +23,10 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m
} }
} }
if len(opts.DefaultBranch) == 0 {
opts.DefaultBranch = setting.Repository.DefaultBranch
}
repo := &models.Repository{ repo := &models.Repository{
OwnerID: u.ID, OwnerID: u.ID,
Owner: u, Owner: u,

View File

@@ -102,7 +102,8 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt
return repo, fmt.Errorf("git.IsEmpty: %v", err) return repo, fmt.Errorf("git.IsEmpty: %v", err)
} }
if !opts.Releases && !repo.IsEmpty { if !repo.IsEmpty {
if len(repo.DefaultBranch) == 0 {
// Try to get HEAD branch and set it as default branch. // Try to get HEAD branch and set it as default branch.
headBranch, err := gitRepo.GetHEADBranch() headBranch, err := gitRepo.GetHEADBranch()
if err != nil { if err != nil {
@@ -111,11 +112,14 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt
if headBranch != nil { if headBranch != nil {
repo.DefaultBranch = headBranch.Name repo.DefaultBranch = headBranch.Name
} }
}
if !opts.Releases {
if err = SyncReleasesWithTags(repo, gitRepo); err != nil { if err = SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Error("Failed to synchronize tags to releases for repository: %v", err) log.Error("Failed to synchronize tags to releases for repository: %v", err)
} }
} }
}
if err = repo.UpdateSize(models.DefaultDBContext()); err != nil { if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
log.Error("Failed to update size for repository: %v", err) log.Error("Failed to update size for repository: %v", err)

View File

@@ -4,8 +4,26 @@
package setting package setting
import "reflect"
// GetCronSettings maps the cron subsection to the provided config // GetCronSettings maps the cron subsection to the provided config
func GetCronSettings(name string, config interface{}) (interface{}, error) { func GetCronSettings(name string, config interface{}) (interface{}, error) {
err := Cfg.Section("cron." + name).MapTo(config) if err := Cfg.Section("cron." + name).MapTo(config); err != nil {
return config, err return config, err
}
typ := reflect.TypeOf(config).Elem()
val := reflect.ValueOf(config).Elem()
for i := 0; i < typ.NumField(); i++ {
field := val.Field(i)
tpField := typ.Field(i)
if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous {
if err := Cfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil {
return config, err
}
}
}
return config, nil
} }

View File

@@ -0,0 +1,47 @@
// Copyright 2020 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 setting
import (
"testing"
"github.com/stretchr/testify/assert"
ini "gopkg.in/ini.v1"
)
func Test_GetCronSettings(t *testing.T) {
type BaseStruct struct {
Base bool
Second string
}
type Extended struct {
BaseStruct
Extend bool
}
iniStr := `
[cron.test]
Base = true
Second = white rabbit
Extend = true
`
Cfg, _ = ini.Load([]byte(iniStr))
extended := &Extended{
BaseStruct: BaseStruct{
Second: "queen of hearts",
},
}
_, err := GetCronSettings("test", extended)
assert.NoError(t, err)
assert.True(t, extended.Base)
assert.EqualValues(t, extended.Second, "white rabbit")
assert.True(t, extended.Extend)
}

View File

@@ -1032,8 +1032,8 @@ func NewContext() {
newMarkup() newMarkup()
sec = Cfg.Section("U2F") sec = Cfg.Section("U2F")
U2F.TrustedFacets, _ = shellquote.Split(sec.Key("TRUSTED_FACETS").MustString(strings.TrimRight(AppURL, "/"))) U2F.TrustedFacets, _ = shellquote.Split(sec.Key("TRUSTED_FACETS").MustString(strings.TrimSuffix(AppURL, AppSubURL+"/")))
U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/")) U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimSuffix(AppURL, "/"))
zip.Verbose = false zip.Verbose = false

View File

@@ -619,7 +619,7 @@ func ActionContent2Commits(act Actioner) *repository.PushCommits {
// DiffTypeToStr returns diff type name // DiffTypeToStr returns diff type name
func DiffTypeToStr(diffType int) string { func DiffTypeToStr(diffType int) string {
diffTypes := map[int]string{ diffTypes := map[int]string{
1: "add", 2: "modify", 3: "del", 4: "rename", 1: "add", 2: "modify", 3: "del", 4: "rename", 5: "copy",
} }
return diffTypes[diffType] return diffTypes[diffType]
} }

View File

@@ -84,7 +84,7 @@ func ListUserOrgs(ctx *context.APIContext) {
if ctx.Written() { if ctx.Written() {
return return
} }
listUserOrgs(ctx, u, ctx.User.IsAdmin) listUserOrgs(ctx, u, ctx.User != nil && (ctx.User.IsAdmin || ctx.User.ID == u.ID))
} }
// GetAll return list of all public organizations // GetAll return list of all public organizations

View File

@@ -256,6 +256,12 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR
return return
} }
// reload repo from db to get a real state after creation
repo, err = models.GetRepositoryByID(repo.ID)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err)
}
ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeOwner)) ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeOwner))
} }

View File

@@ -1956,7 +1956,7 @@ func updateAttachments(item interface{}, files []string) error {
case *models.Comment: case *models.Comment:
attachments = content.Attachments attachments = content.Attachments
default: default:
return fmt.Errorf("Unknow Type") return fmt.Errorf("Unknown Type: %T", content)
} }
for i := 0; i < len(attachments); i++ { for i := 0; i < len(attachments); i++ {
if util.IsStringInSlice(attachments[i].UUID, files) { if util.IsStringInSlice(attachments[i].UUID, files) {
@@ -1974,7 +1974,7 @@ func updateAttachments(item interface{}, files []string) error {
case *models.Comment: case *models.Comment:
err = content.UpdateAttachments(files) err = content.UpdateAttachments(files)
default: default:
return fmt.Errorf("Unknow Type") return fmt.Errorf("Unknown Type: %T", content)
} }
if err != nil { if err != nil {
return err return err
@@ -1986,7 +1986,7 @@ func updateAttachments(item interface{}, files []string) error {
case *models.Comment: case *models.Comment:
content.Attachments, err = models.GetAttachmentsByCommentID(content.ID) content.Attachments, err = models.GetAttachmentsByCommentID(content.ID)
default: default:
return fmt.Errorf("Unknow Type") return fmt.Errorf("Unknown Type: %T", content)
} }
return err return err
} }

View File

@@ -132,7 +132,7 @@ func SingleRelease(ctx *context.Context) {
writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases) writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases)
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
release, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("tag")) release, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("*"))
if err != nil { if err != nil {
if models.IsErrReleaseNotExist(err) { if models.IsErrReleaseNotExist(err) {
ctx.NotFound("GetRelease", err) ctx.NotFound("GetRelease", err)

View File

@@ -185,8 +185,8 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
address = u.String() address = u.String()
if err := mirror_service.SaveAddress(ctx.Repo.Mirror, address); err != nil { if err := mirror_service.UpdateAddress(ctx.Repo.Mirror, address); err != nil {
ctx.ServerError("SaveAddress", err) ctx.ServerError("UpdateAddress", err)
return return
} }

View File

@@ -814,9 +814,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Group("/releases", func() { m.Group("/releases", func() {
m.Get("/", repo.Releases) m.Get("/", repo.Releases)
m.Get("/tag/:tag", repo.SingleRelease) m.Get("/tag/*", repo.SingleRelease)
m.Get("/latest", repo.LatestRelease) m.Get("/latest", repo.LatestRelease)
}, repo.MustBeNotEmpty, context.RepoRef()) }, repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag))
m.Group("/releases", func() { m.Group("/releases", func() {
m.Get("/new", repo.NewRelease) m.Get("/new", repo.NewRelease)
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)

View File

@@ -174,6 +174,7 @@ func NotificationStatusPost(c *context.Context) {
if c.Written() { if c.Written() {
return return
} }
c.Data["Link"] = fmt.Sprintf("%snotifications", setting.AppURL)
c.HTML(http.StatusOK, tplNotificationDiv) c.HTML(http.StatusOK, tplNotificationDiv)
} }

View File

@@ -7,6 +7,7 @@ package user
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"html"
"net/url" "net/url"
"strings" "strings"
@@ -271,8 +272,8 @@ func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) {
ctx.Data["Application"] = app ctx.Data["Application"] = app
ctx.Data["RedirectURI"] = form.RedirectURI ctx.Data["RedirectURI"] = form.RedirectURI
ctx.Data["State"] = form.State ctx.Data["State"] = form.State
ctx.Data["ApplicationUserLink"] = "<a href=\"" + setting.AppURL + app.User.LowerName + "\">@" + app.User.Name + "</a>" ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(setting.AppURL) + html.EscapeString(url.PathEscape(app.User.LowerName)) + "\">@" + html.EscapeString(app.User.Name) + "</a>"
ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + form.RedirectURI + "</strong>" ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + html.EscapeString(form.RedirectURI) + "</strong>"
// TODO document SESSION <=> FORM // TODO document SESSION <=> FORM
err = ctx.Session.Set("client_id", app.ClientID) err = ctx.Session.Set("client_id", app.ClientID)
if err != nil { if err != nil {

View File

@@ -53,6 +53,7 @@ const (
DiffFileChange DiffFileChange
DiffFileDel DiffFileDel
DiffFileRename DiffFileRename
DiffFileCopy
) )
// DiffLineExpandDirection represents the DiffLineSection expand direction // DiffLineExpandDirection represents the DiffLineSection expand direction
@@ -447,7 +448,46 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
} }
line := linebuf.String() line := linebuf.String()
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 { if strings.HasPrefix(line, "--- ") {
if line[4] == '"' {
fmt.Sscanf(line[4:], "%q", &curFile.OldName)
} else {
curFile.OldName = line[4:]
if strings.Contains(curFile.OldName, " ") {
// Git adds a terminal \t if there is a space in the name
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
}
}
if curFile.OldName[0:2] == "a/" {
curFile.OldName = curFile.OldName[2:]
}
continue
} else if strings.HasPrefix(line, "+++ ") {
if line[4] == '"' {
fmt.Sscanf(line[4:], "%q", &curFile.Name)
} else {
curFile.Name = line[4:]
if strings.Contains(curFile.Name, " ") {
// Git adds a terminal \t if there is a space in the name
curFile.Name = curFile.Name[:len(curFile.Name)-1]
}
}
if curFile.Name[0:2] == "b/" {
curFile.Name = curFile.Name[2:]
}
curFile.IsRenamed = (curFile.Name != curFile.OldName) && !(curFile.IsCreated || curFile.IsDeleted)
if curFile.IsDeleted {
curFile.Name = curFile.OldName
curFile.OldName = ""
} else if curFile.IsCreated {
curFile.OldName = ""
}
continue
} else if len(line) == 0 {
continue
}
if strings.HasPrefix(line, "+++") || strings.HasPrefix(line, "---") || len(line) == 0 {
continue continue
} }
@@ -531,36 +571,10 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
break break
} }
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
// e.g. diff --git "a/xxx" "b/xxx"
var a string
var b string
rd := strings.NewReader(line[len(cmdDiffHead):])
char, _ := rd.ReadByte()
_ = rd.UnreadByte()
if char == '"' {
fmt.Fscanf(rd, "%q ", &a)
} else {
fmt.Fscanf(rd, "%s ", &a)
}
char, _ = rd.ReadByte()
_ = rd.UnreadByte()
if char == '"' {
fmt.Fscanf(rd, "%q", &b)
} else {
fmt.Fscanf(rd, "%s", &b)
}
a = a[2:]
b = b[2:]
curFile = &DiffFile{ curFile = &DiffFile{
Name: b,
OldName: a,
Index: len(diff.Files) + 1, Index: len(diff.Files) + 1,
Type: DiffFileChange, Type: DiffFileChange,
Sections: make([]*DiffSection, 0, 10), Sections: make([]*DiffSection, 0, 10),
IsRenamed: a != b,
} }
diff.Files = append(diff.Files, curFile) diff.Files = append(diff.Files, curFile)
curFileLinesCount = 0 curFileLinesCount = 0
@@ -569,6 +583,7 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
curFileLFSPrefix = false curFileLFSPrefix = false
// Check file diff type and is submodule. // Check file diff type and is submodule.
loop:
for { for {
line, err := input.ReadString('\n') line, err := input.ReadString('\n')
if err != nil { if err != nil {
@@ -579,6 +594,7 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
} }
} }
if curFile.Type != DiffFileRename {
switch { switch {
case strings.HasPrefix(line, "new file"): case strings.HasPrefix(line, "new file"):
curFile.Type = DiffFileAdd curFile.Type = DiffFileAdd
@@ -591,12 +607,55 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
case strings.HasPrefix(line, "similarity index 100%"): case strings.HasPrefix(line, "similarity index 100%"):
curFile.Type = DiffFileRename curFile.Type = DiffFileRename
} }
if curFile.Type > 0 { if curFile.Type > 0 && curFile.Type != DiffFileRename {
if strings.HasSuffix(line, " 160000\n") { if strings.HasSuffix(line, " 160000\n") {
curFile.IsSubmodule = true curFile.IsSubmodule = true
} }
break break
} }
} else {
switch {
case strings.HasPrefix(line, "rename from "):
if line[12] == '"' {
fmt.Sscanf(line[12:], "%q", &curFile.OldName)
} else {
curFile.OldName = line[12:]
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
}
case strings.HasPrefix(line, "rename to "):
if line[10] == '"' {
fmt.Sscanf(line[10:], "%q", &curFile.Name)
} else {
curFile.Name = line[10:]
curFile.Name = curFile.Name[:len(curFile.Name)-1]
}
curFile.IsRenamed = true
break loop
case strings.HasPrefix(line, "copy from "):
if line[10] == '"' {
fmt.Sscanf(line[10:], "%q", &curFile.OldName)
} else {
curFile.OldName = line[10:]
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
}
case strings.HasPrefix(line, "copy to "):
if line[8] == '"' {
fmt.Sscanf(line[8:], "%q", &curFile.Name)
} else {
curFile.Name = line[8:]
curFile.Name = curFile.Name[:len(curFile.Name)-1]
}
curFile.IsRenamed = true
curFile.Type = DiffFileCopy
break loop
default:
if strings.HasSuffix(line, " 160000\n") {
curFile.IsSubmodule = true
} else {
break loop
}
}
}
} }
} }
} }

View File

@@ -6,6 +6,7 @@
package gitdiff package gitdiff
import ( import (
"encoding/json"
"fmt" "fmt"
"html/template" "html/template"
"strings" "strings"
@@ -41,7 +42,145 @@ func TestDiffToHTML(t *testing.T) {
}, DiffLineDel)) }, DiffLineDel))
} }
func TestParsePatch(t *testing.T) { func TestParsePatch_singlefile(t *testing.T) {
type testcase struct {
name string
gitdiff string
wantErr bool
addition int
deletion int
oldFilename string
filename string
}
tests := []testcase{
{
name: "readme.md2readme.md",
gitdiff: `diff --git "a/README.md" "b/README.md"
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# gitea-github-migrator
+
+ Build Status
- Latest Release
Docker Pulls
+ cut off
+ cut off
`,
addition: 4,
deletion: 1,
filename: "README.md",
},
{
name: "A \\ B",
gitdiff: `diff --git "a/A \\ B" "b/A \\ B"
--- "a/A \\ B"
+++ "b/A \\ B"
@@ -1,3 +1,6 @@
# gitea-github-migrator
+
+ Build Status
- Latest Release
Docker Pulls
+ cut off
+ cut off`,
addition: 4,
deletion: 1,
filename: "A \\ B",
},
{
name: "really weird filename",
gitdiff: `diff --git a/a b/file b/a a/file b/a b/file b/a a/file
index d2186f1..f5c8ed2 100644
--- a/a b/file b/a a/file
+++ b/a b/file b/a a/file
@@ -1,3 +1,2 @@
Create a weird file.
-and what does diff do here?
\ No newline at end of file`,
addition: 0,
deletion: 1,
filename: "a b/file b/a a/file",
oldFilename: "a b/file b/a a/file",
},
{
name: "delete file with blanks",
gitdiff: `diff --git a/file with blanks b/file with blanks
deleted file mode 100644
index 898651a..0000000
--- a/file with blanks
+++ /dev/null
@@ -1,5 +0,0 @@
-a blank file
-
-has a couple o line
-
-the 5th line is the last
`,
addition: 0,
deletion: 5,
filename: "file with blanks",
},
{
name: "rename a—as",
gitdiff: `diff --git "a/\360\243\220\265b\342\200\240vs" "b/a\342\200\224as"
similarity index 100%
rename from "\360\243\220\265b\342\200\240vs"
rename to "a\342\200\224as"
`,
addition: 0,
deletion: 0,
oldFilename: "𣐵b†vs",
filename: "a—as",
},
{
name: "rename with spaces",
gitdiff: `diff --git a/a b/file b/a a/file b/a b/a a/file b/b file
similarity index 100%
rename from a b/file b/a a/file
rename to a b/a a/file b/b file
`,
oldFilename: "a b/file b/a a/file",
filename: "a b/a a/file b/b file",
},
}
for _, testcase := range tests {
t.Run(testcase.name, func(t *testing.T) {
got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff))
if (err != nil) != testcase.wantErr {
t.Errorf("ParsePatch() error = %v, wantErr %v", err, testcase.wantErr)
return
}
gotMarshaled, _ := json.MarshalIndent(got, " ", " ")
if got.NumFiles() != 1 {
t.Errorf("ParsePath() did not receive 1 file:\n%s", string(gotMarshaled))
return
}
if got.TotalAddition != testcase.addition {
t.Errorf("ParsePath() does not have correct totalAddition %d, wanted %d", got.TotalAddition, testcase.addition)
}
if got.TotalDeletion != testcase.deletion {
t.Errorf("ParsePath() did not have correct totalDeletion %d, wanted %d", got.TotalDeletion, testcase.deletion)
}
file := got.Files[0]
if file.Addition != testcase.addition {
t.Errorf("ParsePath() does not have correct file addition %d, wanted %d", file.Addition, testcase.addition)
}
if file.Deletion != testcase.deletion {
t.Errorf("ParsePath() did not have correct file deletion %d, wanted %d", file.Deletion, testcase.deletion)
}
if file.OldName != testcase.oldFilename {
t.Errorf("ParsePath() did not have correct OldName %s, wanted %s", file.OldName, testcase.oldFilename)
}
if file.Name != testcase.filename {
t.Errorf("ParsePath() did not have correct Name %s, wanted %s", file.Name, testcase.filename)
}
})
}
var diff = `diff --git "a/README.md" "b/README.md" var diff = `diff --git "a/README.md" "b/README.md"
--- a/README.md --- a/README.md
+++ b/README.md +++ b/README.md

View File

@@ -118,7 +118,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, ids []int64, visited map[int
visited[id] = true visited[id] = true
} }
} }
recipients, err := models.GetMaileableUsersByIDs(unique) recipients, err := models.GetMaileableUsersByIDs(unique, fromMention)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -90,8 +90,8 @@ func AddressNoCredentials(m *models.Mirror) string {
return u.String() return u.String()
} }
// SaveAddress writes new address to Git repository config. // UpdateAddress writes new address to Git repository and database
func SaveAddress(m *models.Mirror, addr string) error { func UpdateAddress(m *models.Mirror, addr string) error {
repoPath := m.Repo.RepoPath() repoPath := m.Repo.RepoPath()
// Remove old origin // Remove old origin
_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath) _, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
@@ -99,8 +99,12 @@ func SaveAddress(m *models.Mirror, addr string) error {
return err return err
} }
_, err = git.NewCommand("remote", "add", "origin", "--mirror=fetch", addr).RunInDir(repoPath) if _, err = git.NewCommand("remote", "add", "origin", "--mirror=fetch", addr).RunInDir(repoPath); err != nil {
return err return err
}
m.Repo.OriginalURL = addr
return models.UpdateRepositoryCols(m.Repo, "original_url")
} }
// gitShortEmptySha Git short empty SHA // gitShortEmptySha Git short empty SHA

View File

@@ -9,8 +9,6 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"os"
"path"
"strings" "strings"
"time" "time"
@@ -177,18 +175,6 @@ func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *m
return nil return nil
} }
func addHeadRepoTasks(prs []*models.PullRequest) {
for _, pr := range prs {
log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
if err := PushToBaseRepo(pr); err != nil {
log.Error("PushToBaseRepo: %v", err)
continue
}
AddToTaskQueue(pr)
}
}
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch, // AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
// and generate new patch for testing as needed. // and generate new patch for testing as needed.
func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) { func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) {
@@ -245,7 +231,15 @@ func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSy
} }
} }
addHeadRepoTasks(prs) for _, pr := range prs {
log.Trace("Updating PR[%d]: composing new test task", pr.ID)
if err := PushToBaseRepo(pr); err != nil {
log.Error("PushToBaseRepo: %v", err)
continue
}
AddToTaskQueue(pr)
}
log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch) prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
@@ -345,54 +339,17 @@ func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID st
func PushToBaseRepo(pr *models.PullRequest) (err error) { func PushToBaseRepo(pr *models.PullRequest) (err error) {
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName()) log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
// Clone base repo.
tmpBasePath, err := models.CreateTemporaryPath("pull")
if err != nil {
log.Error("CreateTemporaryPath: %v", err)
return err
}
defer func() {
err := models.RemoveTemporaryPath(tmpBasePath)
if err != nil {
log.Error("Error whilst removing temporary path: %s Error: %v", tmpBasePath, err)
}
}()
if err := pr.LoadHeadRepo(); err != nil { if err := pr.LoadHeadRepo(); err != nil {
log.Error("Unable to load head repository for PR[%d] Error: %v", pr.ID, err) log.Error("Unable to load head repository for PR[%d] Error: %v", pr.ID, err)
return err return err
} }
headRepoPath := pr.HeadRepo.RepoPath() headRepoPath := pr.HeadRepo.RepoPath()
if err := git.Clone(headRepoPath, tmpBasePath, git.CloneRepoOptions{
Bare: true,
Shared: true,
Branch: pr.HeadBranch,
Quiet: true,
}); err != nil {
log.Error("git clone tmpBasePath: %v", err)
return err
}
gitRepo, err := git.OpenRepository(tmpBasePath)
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
}
if err := pr.LoadBaseRepo(); err != nil { if err := pr.LoadBaseRepo(); err != nil {
log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err)
return err return err
} }
if err := gitRepo.AddRemote("base", pr.BaseRepo.RepoPath(), false); err != nil { baseRepoPath := pr.BaseRepo.RepoPath()
return fmt.Errorf("tmpGitRepo.AddRemote: %v", err)
}
defer gitRepo.Close()
headFile := pr.GetGitRefName()
// Remove head in case there is a conflict.
file := path.Join(pr.BaseRepo.RepoPath(), headFile)
_ = os.Remove(file)
if err = pr.LoadIssue(); err != nil { if err = pr.LoadIssue(); err != nil {
return fmt.Errorf("unable to load issue %d for pr %d: %v", pr.IssueID, pr.ID, err) return fmt.Errorf("unable to load issue %d for pr %d: %v", pr.IssueID, pr.ID, err)
@@ -401,24 +358,26 @@ func PushToBaseRepo(pr *models.PullRequest) (err error) {
return fmt.Errorf("unable to load poster %d for pr %d: %v", pr.Issue.PosterID, pr.ID, err) return fmt.Errorf("unable to load poster %d for pr %d: %v", pr.Issue.PosterID, pr.ID, err)
} }
if err = git.Push(tmpBasePath, git.PushOptions{ gitRefName := pr.GetGitRefName()
Remote: "base",
Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile), if err := git.Push(headRepoPath, git.PushOptions{
Remote: baseRepoPath,
Branch: pr.HeadBranch + ":" + gitRefName,
Force: true, Force: true,
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/... // Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo), Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
}); err != nil { }); err != nil {
if git.IsErrPushOutOfDate(err) { if git.IsErrPushOutOfDate(err) {
// This should not happen as we're using force! // This should not happen as we're using force!
log.Error("Unable to push PR head for %s#%d (%-v:%s) due to ErrPushOfDate: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, headFile, err) log.Error("Unable to push PR head for %s#%d (%-v:%s) due to ErrPushOfDate: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, gitRefName, err)
return err return err
} else if git.IsErrPushRejected(err) { } else if git.IsErrPushRejected(err) {
rejectErr := err.(*git.ErrPushRejected) rejectErr := err.(*git.ErrPushRejected)
log.Info("Unable to push PR head for %s#%d (%-v:%s) due to rejection:\nStdout: %s\nStderr: %s\nError: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, headFile, rejectErr.StdOut, rejectErr.StdErr, rejectErr.Err) log.Info("Unable to push PR head for %s#%d (%-v:%s) due to rejection:\nStdout: %s\nStderr: %s\nError: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, gitRefName, rejectErr.StdOut, rejectErr.StdErr, rejectErr.Err)
return err return err
} }
log.Error("Unable to push PR head for %s#%d (%-v:%s) due to Error: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, headFile, err) log.Error("Unable to push PR head for %s#%d (%-v:%s) due to Error: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, gitRefName, err)
return fmt.Errorf("Push: %s:%s %s:%s %v", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), headFile, err) return fmt.Errorf("Push: %s:%s %s:%s %v", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), gitRefName, err)
} }
return nil return nil

View File

@@ -1,4 +1,4 @@
{{range .Attachments}} {{- range .Attachments -}}
<div class="twelve wide column" style="padding: 6px;"> <div class="twelve wide column" style="padding: 6px;">
<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}" title='{{$.ctx.i18n.Tr "repo.issues.attachment.open_tab" .Name}}'> <a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}" title='{{$.ctx.i18n.Tr "repo.issues.attachment.open_tab" .Name}}'>
{{if FilenameIsImage .Name}} {{if FilenameIsImage .Name}}
@@ -12,4 +12,4 @@
<div class="four wide column" style="padding: 0px;"> <div class="four wide column" style="padding: 0px;">
<span class="ui text grey right">{{.Size | FileSize}}</span> <span class="ui text grey right">{{.Size | FileSize}}</span>
</div> </div>
{{end}} {{end -}}

View File

@@ -106,7 +106,7 @@
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
{{$link := printf "%s/commit/%s" $.Repository.HTMLURL $.Issue.PullRequest.MergedCommitID}} {{$link := printf "%s/commit/%s" $.Repository.HTMLURL $.Issue.PullRequest.MergedCommitID}}
{{$.i18n.Tr "repo.issues.pull_merged_at" $link (ShortSha $.Issue.PullRequest.MergedCommitID) $.BaseTarget $createdStr | Str2html}} {{$.i18n.Tr "repo.issues.pull_merged_at" $link (ShortSha $.Issue.PullRequest.MergedCommitID) ($.BaseTarget|Escape) $createdStr | Str2html}}
</span> </span>
</div> </div>
{{else if eq .Type 3 5 6}} {{else if eq .Type 3 5 6}}
@@ -163,7 +163,7 @@
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape|RenderEmoji) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape|RenderEmoji) $createdStr | Safe}}{{end}} {{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|RenderEmoji) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|RenderEmoji) $createdStr | Safe}}{{end}}
</span> </span>
</div> </div>
{{end}} {{end}}

View File

@@ -121,7 +121,7 @@
{{else if .IsPullWorkInProgress}} {{else if .IsPullWorkInProgress}}
<div class="item text grey"> <div class="item text grey">
<i class="icon icon-octicon">{{svg "octicon-x" 16}}</i> <i class="icon icon-octicon">{{svg "octicon-x" 16}}</i>
{{$.i18n.Tr "repo.pulls.cannot_merge_work_in_progress" .WorkInProgressPrefix | Str2html}} {{$.i18n.Tr "repo.pulls.cannot_merge_work_in_progress" (.WorkInProgressPrefix|Escape) | Str2html}}
</div> </div>
{{else if .Issue.PullRequest.IsChecking}} {{else if .Issue.PullRequest.IsChecking}}
<div class="item text yellow"> <div class="item text yellow">

View File

@@ -31,18 +31,18 @@
{{ $mergedStr:= TimeSinceUnix .Issue.PullRequest.MergedUnix $.Lang }} {{ $mergedStr:= TimeSinceUnix .Issue.PullRequest.MergedUnix $.Lang }}
{{if .Issue.OriginalAuthor }} {{if .Issue.OriginalAuthor }}
{{.Issue.OriginalAuthor}} {{.Issue.OriginalAuthor}}
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.merged_title_desc" .NumCommits .HeadTarget .BaseTarget $mergedStr | Str2html}}</span> <span class="pull-desc">{{$.i18n.Tr "repo.pulls.merged_title_desc" .NumCommits (.HeadTarget|Escape) (.BaseTarget|Escape) $mergedStr | Str2html}}</span>
{{else}} {{else}}
<a {{if gt .Issue.PullRequest.Merger.ID 0}}href="{{.Issue.PullRequest.Merger.HomeLink}}"{{end}}>{{.Issue.PullRequest.Merger.GetDisplayName}}</a> <a {{if gt .Issue.PullRequest.Merger.ID 0}}href="{{.Issue.PullRequest.Merger.HomeLink}}"{{end}}>{{.Issue.PullRequest.Merger.GetDisplayName}}</a>
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.merged_title_desc" .NumCommits .HeadTarget .BaseTarget $mergedStr | Str2html}}</span> <span class="pull-desc">{{$.i18n.Tr "repo.pulls.merged_title_desc" .NumCommits (.HeadTarget|Escape) (.BaseTarget|Escape) $mergedStr | Str2html}}</span>
{{end}} {{end}}
{{else}} {{else}}
{{if .Issue.OriginalAuthor }} {{if .Issue.OriginalAuthor }}
<span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}}</span> <span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{$.i18n.Tr "repo.pulls.title_desc" .NumCommits (.HeadTarget|Escape) (.BaseTarget|Escape) | Str2html}}</span>
{{else}} {{else}}
<span id="pull-desc" class="pull-desc"> <span id="pull-desc" class="pull-desc">
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a> <a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>
{{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}} {{$.i18n.Tr "repo.pulls.title_desc" .NumCommits (.HeadTarget|Escape) (.BaseTarget|Escape) | Str2html}}
</span> </span>
{{end}} {{end}}
<span id="pull-desc-edit" style="display: none"> <span id="pull-desc-edit" style="display: none">

View File

@@ -5,7 +5,7 @@
<div class="ui container"> <div class="ui container">
{{template "base/alert" .}} {{template "base/alert" .}}
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.i18n.Tr "repo.settings.branch_protection" .Branch.BranchName | Str2html}} {{.i18n.Tr "repo.settings.branch_protection" (.Branch.BranchName|Escape) | Str2html}}
</h4> </h4>
<div class="ui attached segment branch-protection"> <div class="ui attached segment branch-protection">
<form class="ui form" action="{{.Link}}" method="post"> <form class="ui form" action="{{.Link}}" method="post">

View File

@@ -15,15 +15,15 @@
{{else if .ResendLimited}} {{else if .ResendLimited}}
<p class="center">{{.i18n.Tr "auth.resent_limit_prompt"}}</p> <p class="center">{{.i18n.Tr "auth.resent_limit_prompt"}}</p>
{{else}} {{else}}
<p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" .SignedUser.Email .ActiveCodeLives | Str2html}}</p> <p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" (.SignedUser.Email|Escape) .ActiveCodeLives | Str2html}}</p>
{{end}} {{end}}
{{else}} {{else}}
{{if .IsSendRegisterMail}} {{if .IsSendRegisterMail}}
<p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" .Email .ActiveCodeLives | Str2html}}</p> <p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" (.Email|Escape) .ActiveCodeLives | Str2html}}</p>
{{else if .IsActivateFailed}} {{else if .IsActivateFailed}}
<p>{{.i18n.Tr "auth.invalid_code"}}</p> <p>{{.i18n.Tr "auth.invalid_code"}}</p>
{{else}} {{else}}
<p>{{.i18n.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email | Str2html}}</p> <p>{{.i18n.Tr "auth.has_unconfirmed_mail" (.SignedUser.Name|Escape) (.SignedUser.Email|Escape) | Str2html}}</p>
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="text right"> <div class="text right">
<button class="ui blue button">{{.i18n.Tr "auth.resend_mail"}}</button> <button class="ui blue button">{{.i18n.Tr "auth.resend_mail"}}</button>

View File

@@ -50,17 +50,17 @@
{{$.i18n.Tr "action.reopen_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.reopen_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 16}} {{else if eq .GetOpType 16}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.delete_tag" .GetRepoLink (.GetBranch|Escape) .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 17}} {{else if eq .GetOpType 17}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.delete_branch" .GetRepoLink (.GetBranch|Escape) .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 18}} {{else if eq .GetOpType 18}}
{{ $branchLink := .GetBranch | EscapePound}} {{ $branchLink := .GetBranch | EscapePound}}
{{$.i18n.Tr "action.mirror_sync_push" .GetRepoLink $branchLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.mirror_sync_push" .GetRepoLink $branchLink (.GetBranch|Escape) .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 19}} {{else if eq .GetOpType 19}}
{{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink (.GetBranch|Escape) .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 20}} {{else if eq .GetOpType 20}}
{{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink (.GetBranch|Escape) .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 21}} {{else if eq .GetOpType 21}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}

View File

@@ -1,7 +1,7 @@
language: go language: go
go: go:
- 1.1 - "1.x"
- 1.2 - "1.1"
- 1.3 - "1.4"
- tip - "1.10"

View File

@@ -1,3 +1,5 @@
# This package is no longer being updated! Please look for alternatives if that bothers you.
Resize Resize
====== ======

View File

@@ -78,6 +78,7 @@ var blur = 1.0
// If one of the parameters width or height is set to 0, its size will be calculated so that // If one of the parameters width or height is set to 0, its size will be calculated so that
// the aspect ratio is that of the originating image. // the aspect ratio is that of the originating image.
// The resizing algorithm uses channels for parallel computation. // The resizing algorithm uses channels for parallel computation.
// If the input image has width or height of 0, it is returned unchanged.
func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image { func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image {
scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy())) scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))
if width == 0 { if width == 0 {
@@ -92,6 +93,11 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
return img return img
} }
// Input image has no pixels
if img.Bounds().Dx() <= 0 || img.Bounds().Dy() <= 0 {
return img
}
if interp == NearestNeighbor { if interp == NearestNeighbor {
return resizeNearest(width, height, scaleX, scaleY, img, interp) return resizeNearest(width, height, scaleX, scaleY, img, interp)
} }

402
vendor/github.com/nfnt/resize/ycc.go generated vendored
View File

@@ -88,70 +88,34 @@ func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc {
return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s} return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s}
} }
// Copy of image.YCbCrSubsampleRatio constants - this allows us to support
// older versions of Go where these constants are not defined (i.e. Go 1.4)
const (
ycbcrSubsampleRatio444 image.YCbCrSubsampleRatio = iota
ycbcrSubsampleRatio422
ycbcrSubsampleRatio420
ycbcrSubsampleRatio440
ycbcrSubsampleRatio411
ycbcrSubsampleRatio410
)
// YCbCr converts ycc to a YCbCr image with the same subsample ratio // YCbCr converts ycc to a YCbCr image with the same subsample ratio
// as the YCbCr image that ycc was generated from. // as the YCbCr image that ycc was generated from.
func (p *ycc) YCbCr() *image.YCbCr { func (p *ycc) YCbCr() *image.YCbCr {
ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio) ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
var off int
switch ycbcr.SubsampleRatio { switch ycbcr.SubsampleRatio {
case image.YCbCrSubsampleRatio422: case ycbcrSubsampleRatio422:
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { return p.ycbcr422(ycbcr)
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride case ycbcrSubsampleRatio420:
cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride return p.ycbcr420(ycbcr)
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { case ycbcrSubsampleRatio440:
xx := (x - ycbcr.Rect.Min.X) return p.ycbcr440(ycbcr)
yi := yy + xx case ycbcrSubsampleRatio444:
ci := cy + xx/2 return p.ycbcr444(ycbcr)
ycbcr.Y[yi] = p.Pix[off+0] case ycbcrSubsampleRatio411:
ycbcr.Cb[ci] = p.Pix[off+1] return p.ycbcr411(ycbcr)
ycbcr.Cr[ci] = p.Pix[off+2] case ycbcrSubsampleRatio410:
off += 3 return p.ycbcr410(ycbcr)
}
}
case image.YCbCrSubsampleRatio420:
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
xx := (x - ycbcr.Rect.Min.X)
yi := yy + xx
ci := cy + xx/2
ycbcr.Y[yi] = p.Pix[off+0]
ycbcr.Cb[ci] = p.Pix[off+1]
ycbcr.Cr[ci] = p.Pix[off+2]
off += 3
}
}
case image.YCbCrSubsampleRatio440:
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
xx := (x - ycbcr.Rect.Min.X)
yi := yy + xx
ci := cy + xx
ycbcr.Y[yi] = p.Pix[off+0]
ycbcr.Cb[ci] = p.Pix[off+1]
ycbcr.Cr[ci] = p.Pix[off+2]
off += 3
}
}
default:
// Default to 4:4:4 subsampling.
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
xx := (x - ycbcr.Rect.Min.X)
yi := yy + xx
ci := cy + xx
ycbcr.Y[yi] = p.Pix[off+0]
ycbcr.Cb[ci] = p.Pix[off+1]
ycbcr.Cr[ci] = p.Pix[off+2]
off += 3
}
}
} }
return ycbcr return ycbcr
} }
@@ -159,69 +123,265 @@ func (p *ycc) YCbCr() *image.YCbCr {
// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing. // imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
func imageYCbCrToYCC(in *image.YCbCr) *ycc { func imageYCbCrToYCC(in *image.YCbCr) *ycc {
w, h := in.Rect.Dx(), in.Rect.Dy() w, h := in.Rect.Dx(), in.Rect.Dy()
r := image.Rect(0, 0, w, h) p := ycc{
buf := make([]uint8, 3*w*h) Pix: make([]uint8, 3*w*h),
p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio} Stride: 3 * w,
var off int Rect: image.Rect(0, 0, w, h),
SubsampleRatio: in.SubsampleRatio,
}
switch in.SubsampleRatio { switch in.SubsampleRatio {
case image.YCbCrSubsampleRatio422: case ycbcrSubsampleRatio422:
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { return convertToYCC422(in, &p)
yy := (y - in.Rect.Min.Y) * in.YStride case ycbcrSubsampleRatio420:
cy := (y - in.Rect.Min.Y) * in.CStride return convertToYCC420(in, &p)
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { case ycbcrSubsampleRatio440:
xx := (x - in.Rect.Min.X) return convertToYCC440(in, &p)
yi := yy + xx case ycbcrSubsampleRatio444:
ci := cy + xx/2 return convertToYCC444(in, &p)
p.Pix[off+0] = in.Y[yi] case ycbcrSubsampleRatio411:
p.Pix[off+1] = in.Cb[ci] return convertToYCC411(in, &p)
p.Pix[off+2] = in.Cr[ci] case ycbcrSubsampleRatio410:
off += 3 return convertToYCC410(in, &p)
}
}
case image.YCbCrSubsampleRatio420:
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
yy := (y - in.Rect.Min.Y) * in.YStride
cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
xx := (x - in.Rect.Min.X)
yi := yy + xx
ci := cy + xx/2
p.Pix[off+0] = in.Y[yi]
p.Pix[off+1] = in.Cb[ci]
p.Pix[off+2] = in.Cr[ci]
off += 3
}
}
case image.YCbCrSubsampleRatio440:
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
yy := (y - in.Rect.Min.Y) * in.YStride
cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
xx := (x - in.Rect.Min.X)
yi := yy + xx
ci := cy + xx
p.Pix[off+0] = in.Y[yi]
p.Pix[off+1] = in.Cb[ci]
p.Pix[off+2] = in.Cr[ci]
off += 3
}
}
default:
// Default to 4:4:4 subsampling.
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
yy := (y - in.Rect.Min.Y) * in.YStride
cy := (y - in.Rect.Min.Y) * in.CStride
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
xx := (x - in.Rect.Min.X)
yi := yy + xx
ci := cy + xx
p.Pix[off+0] = in.Y[yi]
p.Pix[off+1] = in.Cb[ci]
p.Pix[off+2] = in.Cr[ci]
off += 3
}
}
} }
return &p return &p
} }
func (p *ycc) ycbcr422(ycbcr *image.YCbCr) *image.YCbCr {
var off int
Pix := p.Pix
Y := ycbcr.Y
Cb := ycbcr.Cb
Cr := ycbcr.Cr
for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
yy := y * ycbcr.YStride
cy := y * ycbcr.CStride
for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
ci := cy + x/2
Y[yy+x] = Pix[off+0]
Cb[ci] = Pix[off+1]
Cr[ci] = Pix[off+2]
off += 3
}
}
return ycbcr
}
func (p *ycc) ycbcr420(ycbcr *image.YCbCr) *image.YCbCr {
var off int
Pix := p.Pix
Y := ycbcr.Y
Cb := ycbcr.Cb
Cr := ycbcr.Cr
for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
yy := y * ycbcr.YStride
cy := (y / 2) * ycbcr.CStride
for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
ci := cy + x/2
Y[yy+x] = Pix[off+0]
Cb[ci] = Pix[off+1]
Cr[ci] = Pix[off+2]
off += 3
}
}
return ycbcr
}
func (p *ycc) ycbcr440(ycbcr *image.YCbCr) *image.YCbCr {
var off int
Pix := p.Pix
Y := ycbcr.Y
Cb := ycbcr.Cb
Cr := ycbcr.Cr
for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
yy := y * ycbcr.YStride
cy := (y / 2) * ycbcr.CStride
for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
ci := cy + x
Y[yy+x] = Pix[off+0]
Cb[ci] = Pix[off+1]
Cr[ci] = Pix[off+2]
off += 3
}
}
return ycbcr
}
func (p *ycc) ycbcr444(ycbcr *image.YCbCr) *image.YCbCr {
var off int
Pix := p.Pix
Y := ycbcr.Y
Cb := ycbcr.Cb
Cr := ycbcr.Cr
for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
yy := y * ycbcr.YStride
cy := y * ycbcr.CStride
for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
ci := cy + x
Y[yy+x] = Pix[off+0]
Cb[ci] = Pix[off+1]
Cr[ci] = Pix[off+2]
off += 3
}
}
return ycbcr
}
func (p *ycc) ycbcr411(ycbcr *image.YCbCr) *image.YCbCr {
var off int
Pix := p.Pix
Y := ycbcr.Y
Cb := ycbcr.Cb
Cr := ycbcr.Cr
for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
yy := y * ycbcr.YStride
cy := y * ycbcr.CStride
for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
ci := cy + x/4
Y[yy+x] = Pix[off+0]
Cb[ci] = Pix[off+1]
Cr[ci] = Pix[off+2]
off += 3
}
}
return ycbcr
}
func (p *ycc) ycbcr410(ycbcr *image.YCbCr) *image.YCbCr {
var off int
Pix := p.Pix
Y := ycbcr.Y
Cb := ycbcr.Cb
Cr := ycbcr.Cr
for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
yy := y * ycbcr.YStride
cy := (y / 2) * ycbcr.CStride
for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
ci := cy + x/4
Y[yy+x] = Pix[off+0]
Cb[ci] = Pix[off+1]
Cr[ci] = Pix[off+2]
off += 3
}
}
return ycbcr
}
func convertToYCC422(in *image.YCbCr, p *ycc) *ycc {
var off int
Pix := p.Pix
Y := in.Y
Cb := in.Cb
Cr := in.Cr
for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
yy := y * in.YStride
cy := y * in.CStride
for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
ci := cy + x/2
Pix[off+0] = Y[yy+x]
Pix[off+1] = Cb[ci]
Pix[off+2] = Cr[ci]
off += 3
}
}
return p
}
func convertToYCC420(in *image.YCbCr, p *ycc) *ycc {
var off int
Pix := p.Pix
Y := in.Y
Cb := in.Cb
Cr := in.Cr
for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
yy := y * in.YStride
cy := (y / 2) * in.CStride
for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
ci := cy + x/2
Pix[off+0] = Y[yy+x]
Pix[off+1] = Cb[ci]
Pix[off+2] = Cr[ci]
off += 3
}
}
return p
}
func convertToYCC440(in *image.YCbCr, p *ycc) *ycc {
var off int
Pix := p.Pix
Y := in.Y
Cb := in.Cb
Cr := in.Cr
for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
yy := y * in.YStride
cy := (y / 2) * in.CStride
for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
ci := cy + x
Pix[off+0] = Y[yy+x]
Pix[off+1] = Cb[ci]
Pix[off+2] = Cr[ci]
off += 3
}
}
return p
}
func convertToYCC444(in *image.YCbCr, p *ycc) *ycc {
var off int
Pix := p.Pix
Y := in.Y
Cb := in.Cb
Cr := in.Cr
for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
yy := y * in.YStride
cy := y * in.CStride
for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
ci := cy + x
Pix[off+0] = Y[yy+x]
Pix[off+1] = Cb[ci]
Pix[off+2] = Cr[ci]
off += 3
}
}
return p
}
func convertToYCC411(in *image.YCbCr, p *ycc) *ycc {
var off int
Pix := p.Pix
Y := in.Y
Cb := in.Cb
Cr := in.Cr
for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
yy := y * in.YStride
cy := y * in.CStride
for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
ci := cy + x/4
Pix[off+0] = Y[yy+x]
Pix[off+1] = Cb[ci]
Pix[off+2] = Cr[ci]
off += 3
}
}
return p
}
func convertToYCC410(in *image.YCbCr, p *ycc) *ycc {
var off int
Pix := p.Pix
Y := in.Y
Cb := in.Cb
Cr := in.Cr
for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
yy := y * in.YStride
cy := (y / 2) * in.CStride
for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
ci := cy + x/4
Pix[off+0] = Y[yy+x]
Pix[off+1] = Cb[ci]
Pix[off+2] = Cr[ci]
off += 3
}
}
return p
}

2
vendor/modules.txt vendored
View File

@@ -518,7 +518,7 @@ github.com/mschoch/smat
# github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc # github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc
## explicit ## explicit
github.com/msteinert/pam github.com/msteinert/pam
# github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 # github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
## explicit ## explicit
github.com/nfnt/resize github.com/nfnt/resize
# github.com/niklasfasching/go-org v0.1.9 # github.com/niklasfasching/go-org v0.1.9