git: migrate to github.com/gogs/git-module@v1.0.0 (#5958)

* WIP

* Finish `internal/db/git_diff.go`

* FInish internal/db/mirror.go

* Finish internal/db/pull.go

* Finish internal/db/release.go

* Finish internal/db/repo.go

* Finish internal/db/repo_branch.go

* Finish internal/db/repo_editor.go

* Finish internal/db/update.go

* Save my work

* Add license header

* Compile!

* Merge master

* Finish internal/cmd/hook.go

* Finish internal/conf/static.go

* Finish internal/context/repo.go

* Finish internal/db/action.go

* Finish internal/db/git_diff.go

* Fix submodule URL inferring

* Finish internal/db/mirror.go

* Updat to beta.4

* css: update fonts

* Finish internal/db/pull.go

* Finish internal/db/release.go

* Finish internal/db/repo_branch.go

* Finish internal/db/wiki.go

* gitutil: enhance infer submodule UR

* Finish internal/route/api/v1/repo/commits.go

* mirror: only collect branch commits after sync

* mirror: fix tag support

* Finish internal/db/repo.go

* Finish internal/db/repo_editor.go

* Finish internal/db/update.go

* Finish internal/gitutil/pull_request.go

* Make it compile

* Finish internal/route/repo/setting.go

* Finish internal/route/repo/branch.go

* Finish internal/route/api/v1/repo/file.go

* Finish internal/route/repo/download.go

* Finish internal/route/repo/editor.go

* Use helper

* Finish internal/route/repo/issue.go

* Finish internal/route/repo/pull.go

* Finish internal/route/repo/release.go

* Finish internal/route/repo/repo.go

* Finish internal/route/repo/wiki.go

* Finish internal/route/repo/commit.go

* Finish internal/route/repo/view.go

* Finish internal/gitutil/tag.go

* go.sum
This commit is contained in:
ᴜɴᴋɴᴡᴏɴ
2020-03-08 19:09:31 +08:00
committed by GitHub
parent c65b5b9f84
commit 6437d0180b
71 changed files with 3874 additions and 3380 deletions

View File

@@ -424,12 +424,12 @@ OLDER_THAN = 24h
[git] [git]
; Disables highlight of added and removed changes ; Disables highlight of added and removed changes
DISABLE_DIFF_HIGHLIGHT = false DISABLE_DIFF_HIGHLIGHT = false
; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100
; Max number of lines allowed of a single file in diff view ; Max number of lines allowed of a single file in diff view
MAX_GIT_DIFF_LINES = 1000 MAX_GIT_DIFF_LINES = 1000
; Max number of characters of a line allowed in diff view ; Max number of characters of a line allowed in diff view
MAX_GIT_DIFF_LINE_CHARACTERS = 500 MAX_GIT_DIFF_LINE_CHARACTERS = 2000
; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100
; Arguments for command 'git gc', e.g. "--aggressive --auto" ; Arguments for command 'git gc', e.g. "--aggressive --auto"
; see more on http://git-scm.com/docs/git-gc/1.7.5 ; see more on http://git-scm.com/docs/git-gc/1.7.5
GC_ARGS = GC_ARGS =

7
go.mod
View File

@@ -5,7 +5,7 @@ go 1.12
require ( require (
github.com/bgentry/speakeasy v0.1.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0 github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0
github.com/editorconfig/editorconfig-core-go/v2 v2.2.1 github.com/editorconfig/editorconfig-core-go/v2 v2.3.0
github.com/fatih/color v1.9.0 // indirect github.com/fatih/color v1.9.0 // indirect
github.com/go-macaron/binding v1.0.1 github.com/go-macaron/binding v1.0.1
github.com/go-macaron/cache v0.0.0-20190810181446-10f7c57e2196 github.com/go-macaron/cache v0.0.0-20190810181446-10f7c57e2196
@@ -18,10 +18,11 @@ require (
github.com/go-sql-driver/mysql v1.4.1 github.com/go-sql-driver/mysql v1.4.1
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/git-module v0.8.3 github.com/gogs/git-module v1.0.0
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a
github.com/google/go-cmp v0.3.0
github.com/google/go-github v17.0.0+incompatible github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
github.com/issue9/identicon v1.0.1 github.com/issue9/identicon v1.0.1
@@ -61,7 +62,7 @@ require (
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.52.0 gopkg.in/ini.v1 v1.53.0
gopkg.in/ldap.v2 v2.5.1 gopkg.in/ldap.v2 v2.5.1
gopkg.in/macaron.v1 v1.3.4 gopkg.in/macaron.v1 v1.3.4
unknwon.dev/clog/v2 v2.1.2 unknwon.dev/clog/v2 v2.1.2

12
go.sum
View File

@@ -41,6 +41,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/editorconfig/editorconfig-core-go/v2 v2.2.1 h1:jY5PCRQf4V0oqpim/Ympl6MwHcb9+nBHEnHOPXqNZ/A= github.com/editorconfig/editorconfig-core-go/v2 v2.2.1 h1:jY5PCRQf4V0oqpim/Ympl6MwHcb9+nBHEnHOPXqNZ/A=
github.com/editorconfig/editorconfig-core-go/v2 v2.2.1/go.mod h1:6XDmqAZsQu8ikS+onLRJfLZvTP3RWTVT8ROX6qcdkio= github.com/editorconfig/editorconfig-core-go/v2 v2.2.1/go.mod h1:6XDmqAZsQu8ikS+onLRJfLZvTP3RWTVT8ROX6qcdkio=
github.com/editorconfig/editorconfig-core-go/v2 v2.3.0 h1:QD1YB/rbntMEQIKM42kQOaqGdS13UvGsl9c8m/nFNWY=
github.com/editorconfig/editorconfig-core-go/v2 v2.3.0/go.mod h1:RNdPfKd9PliYEUZ3r+GxbDsSHNnEluC1wdkQJc3jD4k=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -82,6 +84,10 @@ github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQ
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk= github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
github.com/gogs/git-module v0.8.3 h1:9f8oxSs9OACWrGBYMVnnQNzyTcVN+zzcBM7CXnbmezw= github.com/gogs/git-module v0.8.3 h1:9f8oxSs9OACWrGBYMVnnQNzyTcVN+zzcBM7CXnbmezw=
github.com/gogs/git-module v0.8.3/go.mod h1:aj4tcm7DxaszJWpZLZIRL6gfPXyguAHiE1PDfAAPrCw= github.com/gogs/git-module v0.8.3/go.mod h1:aj4tcm7DxaszJWpZLZIRL6gfPXyguAHiE1PDfAAPrCw=
github.com/gogs/git-module v1.0.0-beta.4 h1:5CyCvTfrb2n5LRpHcNIaFnywHDkM/NxSZVP6t4tpTXI=
github.com/gogs/git-module v1.0.0-beta.4/go.mod h1:oN37FFStFjdnTJXsSbhIHKJXh2YeDsEcXPATVz/oeuQ=
github.com/gogs/git-module v1.0.0 h1:iOlCZ5kPc3RjnWRxdziL5hjCaosYyZw/Lf2odzR/kjw=
github.com/gogs/git-module v1.0.0/go.mod h1:oN37FFStFjdnTJXsSbhIHKJXh2YeDsEcXPATVz/oeuQ=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic= github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4= github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4=
@@ -305,6 +311,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -363,8 +370,11 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AW
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4= gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4=
gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.53.0 h1:c7ruDvTQi0MUTFuNpDRXLSjs7xT4TerM1icIg4uKWRg=
gopkg.in/ini.v1 v1.53.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU=
gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs= gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs=
@@ -375,6 +385,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,7 @@ import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net/url"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@@ -25,7 +26,6 @@ import (
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/email" "gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/httplib" "gogs.io/gogs/internal/httplib"
"gogs.io/gogs/internal/template"
) )
var ( var (
@@ -87,7 +87,7 @@ func runHookPreReceive(c *cli.Context) error {
} }
oldCommitID := string(fields[0]) oldCommitID := string(fields[0])
newCommitID := string(fields[1]) newCommitID := string(fields[1])
branchName := strings.TrimPrefix(string(fields[2]), git.BRANCH_PREFIX) branchName := git.RefShortName(string(fields[2]))
// Branch protection // Branch protection
repoID := com.StrTo(os.Getenv(db.ENV_REPO_ID)).MustInt64() repoID := com.StrTo(os.Getenv(db.ENV_REPO_ID)).MustInt64()
@@ -121,7 +121,7 @@ func runHookPreReceive(c *cli.Context) error {
} }
// check and deletion // check and deletion
if newCommitID == git.EMPTY_SHA { if newCommitID == git.EmptyID {
fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "") fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "")
} }
@@ -221,7 +221,7 @@ func runHookPostReceive(c *cli.Context) error {
options := db.PushUpdateOptions{ options := db.PushUpdateOptions{
OldCommitID: string(fields[0]), OldCommitID: string(fields[0]),
NewCommitID: string(fields[1]), NewCommitID: string(fields[1]),
RefFullName: string(fields[2]), FullRefspec: string(fields[2]),
PusherID: com.StrTo(os.Getenv(db.ENV_AUTH_USER_ID)).MustInt64(), PusherID: com.StrTo(os.Getenv(db.ENV_AUTH_USER_ID)).MustInt64(),
PusherName: os.Getenv(db.ENV_AUTH_USER_NAME), PusherName: os.Getenv(db.ENV_AUTH_USER_NAME),
RepoUserName: os.Getenv(db.ENV_REPO_OWNER_NAME), RepoUserName: os.Getenv(db.ENV_REPO_OWNER_NAME),
@@ -232,19 +232,20 @@ func runHookPostReceive(c *cli.Context) error {
} }
// Ask for running deliver hook and test pull request tasks // Ask for running deliver hook and test pull request tasks
reqURL := conf.Server.LocalRootURL + options.RepoUserName + "/" + options.RepoName + "/tasks/trigger?branch=" + q := make(url.Values)
template.EscapePound(strings.TrimPrefix(options.RefFullName, git.BRANCH_PREFIX)) + q.Add("branch", git.RefShortName(options.FullRefspec))
"&secret=" + os.Getenv(db.ENV_REPO_OWNER_SALT_MD5) + q.Add("secret", os.Getenv(db.ENV_REPO_OWNER_SALT_MD5))
"&pusher=" + os.Getenv(db.ENV_AUTH_USER_ID) q.Add("pusher", os.Getenv(db.ENV_AUTH_USER_ID))
reqURL := fmt.Sprintf("%s%s/%s/tasks/trigger?%s", conf.Server.LocalRootURL, options.RepoUserName, options.RepoName, q.Encode())
log.Trace("Trigger task: %s", reqURL) log.Trace("Trigger task: %s", reqURL)
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{ resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
}).Response() }).Response()
if err == nil { if err == nil {
resp.Body.Close() _ = resp.Body.Close()
if resp.StatusCode/100 != 2 { if resp.StatusCode/100 != 2 {
log.Error("Failed to trigger task: not 2xx response code") log.Error("Failed to trigger task: unsuccessful response code %d", resp.StatusCode)
} }
} else { } else {
log.Error("Failed to trigger task: %v", err) log.Error("Failed to trigger task: %v", err)

View File

@@ -352,9 +352,9 @@ var (
Version string `ini:"-"` Version string `ini:"-"`
DisableDiffHighlight bool DisableDiffHighlight bool
MaxGitDiffLines int MaxDiffFiles int `ini:"MAX_GIT_DIFF_FILES"`
MaxGitDiffLineCharacters int MaxDiffLines int `ini:"MAX_GIT_DIFF_LINES"`
MaxGitDiffFiles int MaxDiffLineChars int `ini:"MAX_GIT_DIFF_LINE_CHARACTERS"`
GCArgs []string `ini:"GC_ARGS" delim:" "` GCArgs []string `ini:"GC_ARGS" delim:" "`
Timeout struct { Timeout struct {
Migrate int Migrate int

View File

@@ -173,7 +173,7 @@ func (c *Context) Handle(status int, msg string, err error) {
c.Data["Title"] = "Page Not Found" c.Data["Title"] = "Page Not Found"
case http.StatusInternalServerError: case http.StatusInternalServerError:
c.Data["Title"] = "Internal Server Error" c.Data["Title"] = "Internal Server Error"
log.Error("%s: %v", msg, err) log.ErrorDepth(5, "%s: %v", msg, err)
if !conf.IsProdMode() || (c.IsLogged && c.User.IsAdmin) { if !conf.IsProdMode() || (c.IsLogged && c.User.IsAdmin) {
c.Data["ErrorMsg"] = err c.Data["ErrorMsg"] = err
} }

View File

@@ -5,19 +5,20 @@
package context package context
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil"
"net/url" "net/url"
"strings" "strings"
"github.com/editorconfig/editorconfig-core-go/v2" "github.com/editorconfig/editorconfig-core-go/v2"
"github.com/pkg/errors"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"github.com/gogs/git-module" "github.com/gogs/git-module"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors" dberrors "gogs.io/gogs/internal/db/errors"
) )
type PullRequest struct { type PullRequest struct {
@@ -75,26 +76,23 @@ func (r *Repository) CanEnableEditor() bool {
return r.Repository.CanEnableEditor() && r.IsViewBranch && r.IsWriter() && !r.Repository.IsBranchRequirePullRequest(r.BranchName) return r.Repository.CanEnableEditor() && r.IsViewBranch && r.IsWriter() && !r.Repository.IsBranchRequirePullRequest(r.BranchName)
} }
// GetEditorconfig returns the .editorconfig definition if found in the // Editorconfig returns the ".editorconfig" definition if found in the HEAD of the default branch.
// HEAD of the default repo branch. func (r *Repository) Editorconfig() (*editorconfig.Editorconfig, error) {
func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { commit, err := r.GitRepo.BranchCommit(r.Repository.DefaultBranch)
commit, err := r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrapf(err, "get commit of branch %q ", r.Repository.DefaultBranch)
} }
treeEntry, err := commit.GetTreeEntryByPath(".editorconfig")
entry, err := commit.TreeEntry(".editorconfig")
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "get .editorconfig")
} }
reader, err := treeEntry.Blob().Data()
p, err := entry.Blob().Bytes()
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "read .editorconfig")
} }
data, err := ioutil.ReadAll(reader) return editorconfig.Parse(bytes.NewReader(p))
if err != nil {
return nil, err
}
return editorconfig.ParseBytes(data)
} }
// MakeURL accepts a string or url.URL as argument and returns escaped URL prepended with repository URL. // MakeURL accepts a string or url.URL as argument and returns escaped URL prepended with repository URL.
@@ -149,7 +147,7 @@ func RepoAssignment(pages ...bool) macaron.Handler {
} else { } else {
owner, err = db.GetUserByName(ownerName) owner, err = db.GetUserByName(ownerName)
if err != nil { if err != nil {
c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err) c.NotFoundOrServerError("GetUserByName", dberrors.IsUserNotExist, err)
return return
} }
} }
@@ -158,7 +156,7 @@ func RepoAssignment(pages ...bool) macaron.Handler {
repo, err := db.GetRepositoryByName(owner.ID, repoName) repo, err := db.GetRepositoryByName(owner.ID, repoName)
if err != nil { if err != nil {
c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err) c.NotFoundOrServerError("GetRepositoryByName", dberrors.IsRepoNotExist, err)
return return
} }
@@ -222,16 +220,16 @@ func RepoAssignment(pages ...bool) macaron.Handler {
c.Data["Mirror"] = c.Repo.Mirror c.Data["Mirror"] = c.Repo.Mirror
} }
gitRepo, err := git.OpenRepository(db.RepoPath(ownerName, repoName)) gitRepo, err := git.Open(db.RepoPath(ownerName, repoName))
if err != nil { if err != nil {
c.ServerError(fmt.Sprintf("RepoAssignment Invalid repo '%s'", c.Repo.Repository.RepoPath()), err) c.ServerError("open repository", err)
return return
} }
c.Repo.GitRepo = gitRepo c.Repo.GitRepo = gitRepo
tags, err := c.Repo.GitRepo.GetTags() tags, err := c.Repo.GitRepo.Tags()
if err != nil { if err != nil {
c.ServerError(fmt.Sprintf("GetTags '%s'", c.Repo.Repository.RepoPath()), err) c.ServerError("get tags", err)
return return
} }
c.Data["Tags"] = tags c.Data["Tags"] = tags
@@ -260,21 +258,21 @@ func RepoAssignment(pages ...bool) macaron.Handler {
} }
c.Data["TagName"] = c.Repo.TagName c.Data["TagName"] = c.Repo.TagName
brs, err := c.Repo.GitRepo.GetBranches() branches, err := c.Repo.GitRepo.Branches()
if err != nil { if err != nil {
c.ServerError("GetBranches", err) c.ServerError("get branches", err)
return return
} }
c.Data["Branches"] = brs c.Data["Branches"] = branches
c.Data["BrancheCount"] = len(brs) c.Data["BrancheCount"] = len(branches)
// If not branch selected, try default one. // If not branch selected, try default one.
// If default branch doesn't exists, fall back to some other branch. // If default branch doesn't exists, fall back to some other branch.
if len(c.Repo.BranchName) == 0 { if len(c.Repo.BranchName) == 0 {
if len(c.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(c.Repo.Repository.DefaultBranch) { if len(c.Repo.Repository.DefaultBranch) > 0 && gitRepo.HasBranch(c.Repo.Repository.DefaultBranch) {
c.Repo.BranchName = c.Repo.Repository.DefaultBranch c.Repo.BranchName = c.Repo.Repository.DefaultBranch
} else if len(brs) > 0 { } else if len(branches) > 0 {
c.Repo.BranchName = brs[0] c.Repo.BranchName = branches[0]
} }
} }
c.Data["BranchName"] = c.Repo.BranchName c.Data["BranchName"] = c.Repo.BranchName
@@ -300,7 +298,7 @@ func RepoRef() macaron.Handler {
// For API calls. // For API calls.
if c.Repo.GitRepo == nil { if c.Repo.GitRepo == nil {
repoPath := db.RepoPath(c.Repo.Owner.Name, c.Repo.Repository.Name) repoPath := db.RepoPath(c.Repo.Owner.Name, c.Repo.Repository.Name)
c.Repo.GitRepo, err = git.OpenRepository(repoPath) c.Repo.GitRepo, err = git.Open(repoPath)
if err != nil { if err != nil {
c.Handle(500, "RepoRef Invalid repo "+repoPath, err) c.Handle(500, "RepoRef Invalid repo "+repoPath, err)
return return
@@ -310,17 +308,17 @@ func RepoRef() macaron.Handler {
// Get default branch. // Get default branch.
if len(c.Params("*")) == 0 { if len(c.Params("*")) == 0 {
refName = c.Repo.Repository.DefaultBranch refName = c.Repo.Repository.DefaultBranch
if !c.Repo.GitRepo.IsBranchExist(refName) { if !c.Repo.GitRepo.HasBranch(refName) {
brs, err := c.Repo.GitRepo.GetBranches() branches, err := c.Repo.GitRepo.Branches()
if err != nil { if err != nil {
c.Handle(500, "GetBranches", err) c.ServerError("get branches", err)
return return
} }
refName = brs[0] refName = branches[0]
} }
c.Repo.Commit, err = c.Repo.GitRepo.GetBranchCommit(refName) c.Repo.Commit, err = c.Repo.GitRepo.BranchCommit(refName)
if err != nil { if err != nil {
c.Handle(500, "GetBranchCommit", err) c.ServerError("get branch commit", err)
return return
} }
c.Repo.CommitID = c.Repo.Commit.ID.String() c.Repo.CommitID = c.Repo.Commit.ID.String()
@@ -332,8 +330,8 @@ func RepoRef() macaron.Handler {
for i, part := range parts { for i, part := range parts {
refName = strings.TrimPrefix(refName+"/"+part, "/") refName = strings.TrimPrefix(refName+"/"+part, "/")
if c.Repo.GitRepo.IsBranchExist(refName) || if c.Repo.GitRepo.HasBranch(refName) ||
c.Repo.GitRepo.IsTagExist(refName) { c.Repo.GitRepo.HasTag(refName) {
if i < len(parts)-1 { if i < len(parts)-1 {
c.Repo.TreePath = strings.Join(parts[i+1:], "/") c.Repo.TreePath = strings.Join(parts[i+1:], "/")
} }
@@ -346,21 +344,21 @@ func RepoRef() macaron.Handler {
c.Repo.TreePath = strings.Join(parts[1:], "/") c.Repo.TreePath = strings.Join(parts[1:], "/")
} }
if c.Repo.GitRepo.IsBranchExist(refName) { if c.Repo.GitRepo.HasBranch(refName) {
c.Repo.IsViewBranch = true c.Repo.IsViewBranch = true
c.Repo.Commit, err = c.Repo.GitRepo.GetBranchCommit(refName) c.Repo.Commit, err = c.Repo.GitRepo.BranchCommit(refName)
if err != nil { if err != nil {
c.Handle(500, "GetBranchCommit", err) c.ServerError("get branch commit", err)
return return
} }
c.Repo.CommitID = c.Repo.Commit.ID.String() c.Repo.CommitID = c.Repo.Commit.ID.String()
} else if c.Repo.GitRepo.IsTagExist(refName) { } else if c.Repo.GitRepo.HasTag(refName) {
c.Repo.IsViewTag = true c.Repo.IsViewTag = true
c.Repo.Commit, err = c.Repo.GitRepo.GetTagCommit(refName) c.Repo.Commit, err = c.Repo.GitRepo.TagCommit(refName)
if err != nil { if err != nil {
c.Handle(500, "GetTagCommit", err) c.ServerError("get tag commit", err)
return return
} }
c.Repo.CommitID = c.Repo.Commit.ID.String() c.Repo.CommitID = c.Repo.Commit.ID.String()
@@ -368,7 +366,7 @@ func RepoRef() macaron.Handler {
c.Repo.IsViewCommit = true c.Repo.IsViewCommit = true
c.Repo.CommitID = refName c.Repo.CommitID = refName
c.Repo.Commit, err = c.Repo.GitRepo.GetCommit(refName) c.Repo.Commit, err = c.Repo.GitRepo.CatFileCommit(refName)
if err != nil { if err != nil {
c.NotFound() c.NotFound()
return return

View File

@@ -268,9 +268,9 @@ func (pc *PushCommits) ToApiPayloadCommits(repoPath, repoURL string) ([]*api.Pay
return nil, fmt.Errorf("GetUserByEmail: %v", err) return nil, fmt.Errorf("GetUserByEmail: %v", err)
} }
fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1) nameStatus, err := git.RepoShowNameStatus(repoPath, commit.Sha1)
if err != nil { if err != nil {
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %v", commit.Sha1, err) return nil, fmt.Errorf("show name status [commit_sha1: %s]: %v", commit.Sha1, err)
} }
commits[i] = &api.PayloadCommit{ commits[i] = &api.PayloadCommit{
@@ -287,9 +287,9 @@ func (pc *PushCommits) ToApiPayloadCommits(repoPath, repoURL string) ([]*api.Pay
Email: commit.CommitterEmail, Email: commit.CommitterEmail,
UserName: committerUsername, UserName: committerUsername,
}, },
Added: fileStatus.Added, Added: nameStatus.Added,
Removed: fileStatus.Removed, Removed: nameStatus.Removed,
Modified: fileStatus.Modified, Modified: nameStatus.Modified,
Timestamp: commit.Timestamp, Timestamp: commit.Timestamp,
} }
} }
@@ -298,21 +298,21 @@ func (pc *PushCommits) ToApiPayloadCommits(repoPath, repoURL string) ([]*api.Pay
// AvatarLink tries to match user in database with e-mail // AvatarLink tries to match user in database with e-mail
// in order to show custom avatar, and falls back to general avatar link. // in order to show custom avatar, and falls back to general avatar link.
func (push *PushCommits) AvatarLink(email string) string { func (pcs *PushCommits) AvatarLink(email string) string {
_, ok := push.avatars[email] _, ok := pcs.avatars[email]
if !ok { if !ok {
u, err := GetUserByEmail(email) u, err := GetUserByEmail(email)
if err != nil { if err != nil {
push.avatars[email] = tool.AvatarLink(email) pcs.avatars[email] = tool.AvatarLink(email)
if !errors.IsUserNotExist(err) { if !errors.IsUserNotExist(err) {
log.Error("GetUserByEmail: %v", err) log.Error("GetUserByEmail: %v", err)
} }
} else { } else {
push.avatars[email] = u.RelAvatarLink() pcs.avatars[email] = u.RelAvatarLink()
} }
} }
return push.avatars[email] return pcs.avatars[email]
} }
// UpdateIssuesCommit checks if issues are manipulated by commit message. // UpdateIssuesCommit checks if issues are manipulated by commit message.
@@ -474,12 +474,12 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("UpdateRepository: %v", err) return fmt.Errorf("UpdateRepository: %v", err)
} }
isNewRef := opts.OldCommitID == git.EMPTY_SHA isNewRef := opts.OldCommitID == git.EmptyID
isDelRef := opts.NewCommitID == git.EMPTY_SHA isDelRef := opts.NewCommitID == git.EmptyID
opType := ACTION_COMMIT_REPO opType := ACTION_COMMIT_REPO
// Check if it's tag push or branch. // Check if it's tag push or branch.
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) { if strings.HasPrefix(opts.RefFullName, git.RefsTags) {
opType = ACTION_PUSH_TAG opType = ACTION_PUSH_TAG
} else { } else {
// if not the first commit, set the compare URL. // if not the first commit, set the compare URL.
@@ -504,7 +504,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("Marshal: %v", err) return fmt.Errorf("Marshal: %v", err)
} }
refName := git.RefEndName(opts.RefFullName) refName := git.RefShortName(opts.RefFullName)
action := &Action{ action := &Action{
ActUserID: pusher.ID, ActUserID: pusher.ID,
ActUserName: pusher.Name, ActUserName: pusher.Name,

View File

@@ -1,194 +0,0 @@
// Copyright 2014 The Gogs 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 db
import (
"bytes"
"fmt"
"html"
"html/template"
"io"
"github.com/sergi/go-diff/diffmatchpatch"
"golang.org/x/net/html/charset"
"golang.org/x/text/transform"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/template/highlight"
"gogs.io/gogs/internal/tool"
)
type DiffSection struct {
*git.DiffSection
}
var (
addedCodePrefix = []byte("<span class=\"added-code\">")
removedCodePrefix = []byte("<span class=\"removed-code\">")
codeTagSuffix = []byte("</span>")
)
func diffToHTML(diffs []diffmatchpatch.Diff, lineType git.DiffLineType) template.HTML {
buf := bytes.NewBuffer(nil)
// Reproduce signs which are cutted for inline diff before.
switch lineType {
case git.DIFF_LINE_ADD:
buf.WriteByte('+')
case git.DIFF_LINE_DEL:
buf.WriteByte('-')
}
for i := range diffs {
switch {
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == git.DIFF_LINE_ADD:
buf.Write(addedCodePrefix)
buf.WriteString(html.EscapeString(diffs[i].Text))
buf.Write(codeTagSuffix)
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == git.DIFF_LINE_DEL:
buf.Write(removedCodePrefix)
buf.WriteString(html.EscapeString(diffs[i].Text))
buf.Write(codeTagSuffix)
case diffs[i].Type == diffmatchpatch.DiffEqual:
buf.WriteString(html.EscapeString(diffs[i].Text))
}
}
return template.HTML(buf.Bytes())
}
var diffMatchPatch = diffmatchpatch.New()
func init() {
diffMatchPatch.DiffEditCost = 100
}
// ComputedInlineDiffFor computes inline diff for the given line.
func (diffSection *DiffSection) ComputedInlineDiffFor(diffLine *git.DiffLine) template.HTML {
if conf.Git.DisableDiffHighlight {
return template.HTML(html.EscapeString(diffLine.Content[1:]))
}
var (
compareDiffLine *git.DiffLine
diff1 string
diff2 string
)
// try to find equivalent diff line. ignore, otherwise
switch diffLine.Type {
case git.DIFF_LINE_ADD:
compareDiffLine = diffSection.Line(git.DIFF_LINE_DEL, diffLine.RightIdx)
if compareDiffLine == nil {
return template.HTML(html.EscapeString(diffLine.Content))
}
diff1 = compareDiffLine.Content
diff2 = diffLine.Content
case git.DIFF_LINE_DEL:
compareDiffLine = diffSection.Line(git.DIFF_LINE_ADD, diffLine.LeftIdx)
if compareDiffLine == nil {
return template.HTML(html.EscapeString(diffLine.Content))
}
diff1 = diffLine.Content
diff2 = compareDiffLine.Content
default:
return template.HTML(html.EscapeString(diffLine.Content))
}
diffRecord := diffMatchPatch.DiffMain(diff1[1:], diff2[1:], true)
diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
return diffToHTML(diffRecord, diffLine.Type)
}
type DiffFile struct {
*git.DiffFile
Sections []*DiffSection
}
func (diffFile *DiffFile) HighlightClass() string {
return highlight.FileNameToHighlightClass(diffFile.Name)
}
type Diff struct {
*git.Diff
Files []*DiffFile
}
func NewDiff(gitDiff *git.Diff) *Diff {
diff := &Diff{
Diff: gitDiff,
Files: make([]*DiffFile, gitDiff.NumFiles()),
}
// FIXME: detect encoding while parsing.
var buf bytes.Buffer
for i := range gitDiff.Files {
buf.Reset()
diff.Files[i] = &DiffFile{
DiffFile: gitDiff.Files[i],
Sections: make([]*DiffSection, gitDiff.Files[i].NumSections()),
}
for j := range gitDiff.Files[i].Sections {
diff.Files[i].Sections[j] = &DiffSection{
DiffSection: gitDiff.Files[i].Sections[j],
}
for k := range diff.Files[i].Sections[j].Lines {
buf.WriteString(diff.Files[i].Sections[j].Lines[k].Content)
buf.WriteString("\n")
}
}
charsetLabel, err := tool.DetectEncoding(buf.Bytes())
if charsetLabel != "UTF-8" && err == nil {
encoding, _ := charset.Lookup(charsetLabel)
if encoding != nil {
d := encoding.NewDecoder()
for j := range diff.Files[i].Sections {
for k := range diff.Files[i].Sections[j].Lines {
if c, _, err := transform.String(d, diff.Files[i].Sections[j].Lines[k].Content); err == nil {
diff.Files[i].Sections[j].Lines[k].Content = c
}
}
}
}
}
}
return diff
}
func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) {
done := make(chan error)
var gitDiff *git.Diff
go func() {
gitDiff = git.ParsePatch(done, maxLines, maxLineCharacteres, maxFiles, reader)
}()
if err := <-done; err != nil {
return nil, fmt.Errorf("ParsePatch: %v", err)
}
return NewDiff(gitDiff), nil
}
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
gitDiff, err := git.GetDiffRange(repoPath, beforeCommitID, afterCommitID, maxLines, maxLineCharacteres, maxFiles)
if err != nil {
return nil, fmt.Errorf("GetDiffRange: %v", err)
}
return NewDiff(gitDiff), nil
}
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
gitDiff, err := git.GetDiffCommit(repoPath, commitID, maxLines, maxLineCharacteres, maxFiles)
if err != nil {
return nil, fmt.Errorf("GetDiffCommit: %v", err)
}
return NewDiff(gitDiff), nil
}

View File

@@ -1,35 +0,0 @@
// Copyright 2016 The Gogs 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 db
import (
"html/template"
"testing"
"github.com/gogs/git-module"
dmp "github.com/sergi/go-diff/diffmatchpatch"
)
func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
if s1 != string(s2) {
t.Errorf("%s should be equal %s", s2, s1)
}
}
func Test_diffToHTML(t *testing.T) {
assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
{Type: dmp.DiffEqual, Text: "foo "},
{Type: dmp.DiffInsert, Text: "bar"},
{Type: dmp.DiffDelete, Text: " baz"},
{Type: dmp.DiffEqual, Text: " biz"},
}, git.DiffLineAdd))
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
{Type: dmp.DiffEqual, Text: "foo "},
{Type: dmp.DiffDelete, Text: "bar"},
{Type: dmp.DiffInsert, Text: " baz"},
{Type: dmp.DiffEqual, Text: " biz"},
}, git.DiffLineDel))
}

View File

@@ -18,7 +18,7 @@ import (
) )
func updateRepositorySizes(x *xorm.Engine) (err error) { func updateRepositorySizes(x *xorm.Engine) (err error) {
log.Info("This migration could take up to minutes, please be patient.") log.Info("[migrations.v16] This migration could take up to minutes, please be patient.")
type Repository struct { type Repository struct {
ID int64 ID int64
OwnerID int64 OwnerID int64
@@ -41,7 +41,7 @@ func updateRepositorySizes(x *xorm.Engine) (err error) {
Find(&repos); err != nil { Find(&repos); err != nil {
return fmt.Errorf("select repos [offset: %d]: %v", offset, err) return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
} }
log.Trace("Select [offset: %d, repos: %d]", offset, len(repos)) log.Trace("[migrations.v16] Select [offset: %d, repos: %d]", offset, len(repos))
if len(repos) == 0 { if len(repos) == 0 {
break break
} }
@@ -60,10 +60,10 @@ func updateRepositorySizes(x *xorm.Engine) (err error) {
continue continue
} }
repoPath := filepath.Join(conf.Repository.Root, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git" repoPath := strings.ToLower(filepath.Join(conf.Repository.Root, user.Name, repo.Name)) + ".git"
countObject, err := git.GetRepoSize(repoPath) countObject, err := git.RepoCountObjects(repoPath)
if err != nil { if err != nil {
log.Warn("GetRepoSize: %v", err) log.Warn("[migrations.v16] Count repository objects: %v", err)
continue continue
} }

View File

@@ -5,7 +5,6 @@
package db package db
import ( import (
"container/list"
"fmt" "fmt"
"net/url" "net/url"
"strings" "strings"
@@ -72,54 +71,17 @@ func (m *Mirror) ScheduleNextSync() {
m.NextSync = time.Now().Add(time.Duration(m.Interval) * time.Hour) m.NextSync = time.Now().Add(time.Duration(m.Interval) * time.Hour)
} }
// findPasswordInMirrorAddress returns start (inclusive) and end index (exclusive)
// of password portion of credentials in given mirror address.
// It returns a boolean value to indicate whether password portion is found.
func findPasswordInMirrorAddress(addr string) (start int, end int, found bool) {
// Find end of credentials (start of path)
end = strings.LastIndex(addr, "@")
if end == -1 {
return -1, -1, false
}
// Find delimiter of credentials (end of username)
start = strings.Index(addr, "://")
if start == -1 {
return -1, -1, false
}
start += 3
delim := strings.Index(addr[start:], ":")
if delim == -1 {
return -1, -1, false
}
delim += 1
if start+delim >= end {
return -1, -1, false // No password portion presented
}
return start + delim, end, true
}
// unescapeMirrorCredentials returns mirror address with unescaped credentials.
func unescapeMirrorCredentials(addr string) string {
start, end, found := findPasswordInMirrorAddress(addr)
if !found {
return addr
}
password, _ := url.QueryUnescape(addr[start:end])
return addr[:start] + password + addr[end:]
}
func (m *Mirror) readAddress() { func (m *Mirror) readAddress() {
if len(m.address) > 0 { if len(m.address) > 0 {
return return
} }
cfg, err := ini.Load(m.Repo.GitConfigPath()) cfg, err := ini.LoadSources(
ini.LoadOptions{IgnoreInlineComment: true},
m.Repo.GitConfigPath(),
)
if err != nil { if err != nil {
log.Error("Load: %v", err) log.Error("load config: %v", err)
return return
} }
m.address = cfg.Section("remote \"origin\"").Key("url").Value() m.address = cfg.Section("remote \"origin\"").Key("url").Value()
@@ -128,6 +90,7 @@ func (m *Mirror) readAddress() {
// HandleMirrorCredentials replaces user credentials from HTTP/HTTPS URL // HandleMirrorCredentials replaces user credentials from HTTP/HTTPS URL
// with placeholder <credentials>. // with placeholder <credentials>.
// It returns original string if protocol is not HTTP/HTTPS. // It returns original string if protocol is not HTTP/HTTPS.
// TODO(unknwon): Use url.Parse.
func HandleMirrorCredentials(url string, mosaics bool) string { func HandleMirrorCredentials(url string, mosaics bool) string {
i := strings.Index(url, "@") i := strings.Index(url, "@")
if i == -1 { if i == -1 {
@@ -161,34 +124,21 @@ func (m *Mirror) RawAddress() string {
return m.address return m.address
} }
// FullAddress returns mirror address from Git repository config with unescaped credentials.
func (m *Mirror) FullAddress() string {
m.readAddress()
return unescapeMirrorCredentials(m.address)
}
// escapeCredentials returns mirror address with escaped credentials.
func escapeMirrorCredentials(addr string) string {
start, end, found := findPasswordInMirrorAddress(addr)
if !found {
return addr
}
return addr[:start] + url.QueryEscape(addr[start:end]) + addr[end:]
}
// SaveAddress writes new address to Git repository config. // SaveAddress writes new address to Git repository config.
func (m *Mirror) SaveAddress(addr string) error { func (m *Mirror) SaveAddress(addr string) error {
repoPath := m.Repo.RepoPath() repoPath := m.Repo.RepoPath()
err := git.RemoveRemote(repoPath, "origin") err := git.RepoRemoveRemote(repoPath, "origin")
if err != nil { if err != nil {
return fmt.Errorf("remove remote 'origin': %v", err) return fmt.Errorf("remove remote 'origin': %v", err)
} }
err = git.AddRemote(repoPath, "origin", addr, git.AddRemoteOptions{ addrURL, err := url.Parse(addr)
Mirror: true, if err != nil {
}) return err
}
err = git.RepoAddRemote(repoPath, "origin", addrURL.String(), git.AddRemoteOptions{MirrorFetch: true})
if err != nil { if err != nil {
return fmt.Errorf("add remote 'origin': %v", err) return fmt.Errorf("add remote 'origin': %v", err)
} }
@@ -196,7 +146,7 @@ func (m *Mirror) SaveAddress(addr string) error {
return nil return nil
} }
const GIT_SHORT_EMPTY_SHA = "0000000" const gitShortEmptyID = "0000000"
// mirrorSyncResult contains information of a updated reference. // mirrorSyncResult contains information of a updated reference.
// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. // If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty.
@@ -223,12 +173,12 @@ func parseRemoteUpdateOutput(output string) []*mirrorSyncResult {
case strings.HasPrefix(lines[i], " * "): // New reference case strings.HasPrefix(lines[i], " * "): // New reference
results = append(results, &mirrorSyncResult{ results = append(results, &mirrorSyncResult{
refName: refName, refName: refName,
oldCommitID: GIT_SHORT_EMPTY_SHA, oldCommitID: gitShortEmptyID,
}) })
case strings.HasPrefix(lines[i], " - "): // Delete reference case strings.HasPrefix(lines[i], " - "): // Delete reference
results = append(results, &mirrorSyncResult{ results = append(results, &mirrorSyncResult{
refName: refName, refName: refName,
newCommitID: GIT_SHORT_EMPTY_SHA, newCommitID: gitShortEmptyID,
}) })
case strings.HasPrefix(lines[i], " "): // New commits of a reference case strings.HasPrefix(lines[i], " "): // New commits of a reference
delimIdx := strings.Index(lines[i][3:], " ") delimIdx := strings.Index(lines[i][3:], " ")
@@ -262,10 +212,7 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) {
// Do a fast-fail testing against on repository URL to ensure it is accessible under // Do a fast-fail testing against on repository URL to ensure it is accessible under
// good condition to prevent long blocking on URL resolution without syncing anything. // good condition to prevent long blocking on URL resolution without syncing anything.
if !git.IsRepoURLAccessible(git.NetworkOptions{ if !git.IsURLAccessible(time.Minute, m.RawAddress()) {
URL: m.RawAddress(),
Timeout: 10 * time.Second,
}) {
desc := fmt.Sprintf("Source URL of mirror repository '%s' is not accessible: %s", m.Repo.FullName(), m.MosaicsAddress()) desc := fmt.Sprintf("Source URL of mirror repository '%s' is not accessible: %s", m.Repo.FullName(), m.MosaicsAddress())
if err := CreateRepositoryNotice(desc); err != nil { if err := CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err) log.Error("CreateRepositoryNotice: %v", err)
@@ -393,15 +340,14 @@ func SyncMirrors() {
// - Create "Mirror Sync" webhook event // - Create "Mirror Sync" webhook event
// - Create mirror sync (create, push and delete) events and trigger the "mirror sync" webhooks // - Create mirror sync (create, push and delete) events and trigger the "mirror sync" webhooks
var gitRepo *git.Repository
if len(results) == 0 { if len(results) == 0 {
log.Trace("SyncMirrors [repo_id: %d]: no commits fetched", m.RepoID) log.Trace("SyncMirrors [repo_id: %d]: no commits fetched", m.RepoID)
} else {
gitRepo, err = git.OpenRepository(m.Repo.RepoPath())
if err != nil {
log.Error("OpenRepository [%d]: %v", m.RepoID, err)
continue
} }
gitRepo, err := git.Open(m.Repo.RepoPath())
if err != nil {
log.Error("Failed to open repository [repo_id: %d]: %v", m.RepoID, err)
continue
} }
for _, result := range results { for _, result := range results {
@@ -411,7 +357,7 @@ func SyncMirrors() {
} }
// Delete reference // Delete reference
if result.newCommitID == GIT_SHORT_EMPTY_SHA { if result.newCommitID == gitShortEmptyID {
if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil { if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil {
log.Error("MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err) log.Error("MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err)
} }
@@ -420,7 +366,7 @@ func SyncMirrors() {
// New reference // New reference
isNewRef := false isNewRef := false
if result.oldCommitID == GIT_SHORT_EMPTY_SHA { if result.oldCommitID == gitShortEmptyID {
if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil { if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil {
log.Error("MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err) log.Error("MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err)
continue continue
@@ -429,49 +375,52 @@ func SyncMirrors() {
} }
// Push commits // Push commits
var commits *list.List var commits []*git.Commit
var oldCommitID string var oldCommitID string
var newCommitID string var newCommitID string
if !isNewRef { if !isNewRef {
oldCommitID, err = git.GetFullCommitID(gitRepo.Path, result.oldCommitID) oldCommitID, err = gitRepo.RevParse(result.oldCommitID)
if err != nil { if err != nil {
log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) log.Error("Failed to parse revision [repo_id: %d, old_commit_id: %s]: %v", m.RepoID, result.oldCommitID, err)
continue continue
} }
newCommitID, err = git.GetFullCommitID(gitRepo.Path, result.newCommitID) newCommitID, err = gitRepo.RevParse(result.newCommitID)
if err != nil { if err != nil {
log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) log.Error("Failed to parse revision [repo_id: %d, new_commit_id: %s]: %v", m.RepoID, result.newCommitID, err)
continue continue
} }
commits, err = gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) commits, err = gitRepo.RevList([]string{oldCommitID + "..." + newCommitID})
if err != nil { if err != nil {
log.Error("CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err) log.Error("Failed to list commits [repo_id: %d, old_commit_id: %s, new_commit_id: %s]: %v", m.RepoID, oldCommitID, newCommitID, err)
continue continue
} }
} else {
refNewCommitID, err := gitRepo.GetBranchCommitID(result.refName) } else if gitRepo.HasBranch(result.refName) {
refNewCommit, err := gitRepo.BranchCommit(result.refName)
if err != nil { if err != nil {
log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) log.Error("Failed to get branch commit [repo_id: %d, branch: %s]: %v", m.RepoID, result.refName, err)
continue continue
} }
if newCommit, err := gitRepo.GetCommit(refNewCommitID); err != nil {
log.Error("GetCommit [repo_id: %d, commit_id: %s]: %v", m.RepoID, refNewCommitID, err) // TODO(unknwon): Get the commits for the new ref until the closest ancestor branch like GitHub does.
continue commits, err = refNewCommit.Ancestors(git.LogOptions{MaxCount: 9})
} else {
// TODO: Get the commits for the new ref until the closest ancestor branch like Github does
commits, err = newCommit.CommitsBeforeLimit(10)
if err != nil { if err != nil {
log.Error("CommitsBeforeLimit [repo_id: %d, commit_id: %s]: %v", m.RepoID, refNewCommitID, err) log.Error("Failed to get ancestors [repo_id: %d, commit_id: %s]: %v", m.RepoID, refNewCommit.ID, err)
} continue
oldCommitID = git.EMPTY_SHA
newCommitID = refNewCommitID
} }
// Put the latest commit in front of ancestors
commits = append([]*git.Commit{refNewCommit}, commits...)
oldCommitID = git.EmptyID
newCommitID = refNewCommit.ID.String()
} }
if err = MirrorSyncPushAction(m.Repo, MirrorSyncPushActionOptions{ if err = MirrorSyncPushAction(m.Repo, MirrorSyncPushActionOptions{
RefName: result.refName, RefName: result.refName,
OldCommitID: oldCommitID, OldCommitID: oldCommitID,
NewCommitID: newCommitID, NewCommitID: newCommitID,
Commits: ListToPushCommits(commits), Commits: CommitsToPushCommits(commits),
}); err != nil { }); err != nil {
log.Error("MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err) log.Error("MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err)
continue continue
@@ -485,15 +434,15 @@ func SyncMirrors() {
// Get latest commit date and compare to current repository updated time, // Get latest commit date and compare to current repository updated time,
// update if latest commit date is newer. // update if latest commit date is newer.
commitDate, err := git.GetLatestCommitDate(m.Repo.RepoPath(), "") latestCommitTime, err := gitRepo.LatestCommitTime()
if err != nil { if err != nil {
log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err) log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err)
continue continue
} else if commitDate.Before(m.Repo.Updated) { } else if !latestCommitTime.After(m.Repo.Updated) {
continue continue
} }
if _, err = x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", commitDate.Unix(), m.RepoID); err != nil { if _, err = x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", latestCommitTime.Unix(), m.RepoID); err != nil {
log.Error("Update 'repository.updated_unix' [%d]: %v", m.RepoID, err) log.Error("Update 'repository.updated_unix' [%d]: %v", m.RepoID, err)
continue continue
} }

View File

@@ -7,14 +7,13 @@ package db
import ( import (
"testing" "testing"
. "github.com/smartystreets/goconvey/convey" "github.com/stretchr/testify/assert"
) )
func Test_parseRemoteUpdateOutput(t *testing.T) { func Test_parseRemoteUpdateOutput(t *testing.T) {
Convey("Parse mirror remote update output", t, func() { tests := []struct {
testCases := []struct {
output string output string
results []*mirrorSyncResult expResults []*mirrorSyncResult
}{ }{
{ {
` `
@@ -24,85 +23,15 @@ From https://try.gogs.io/unknwon/upsteam
- [deleted] (none) -> bugfix - [deleted] (none) -> bugfix
`, `,
[]*mirrorSyncResult{ []*mirrorSyncResult{
{"develop", GIT_SHORT_EMPTY_SHA, ""}, {"develop", gitShortEmptyID, ""},
{"master", "b0bb24f", "1d85a4f"}, {"master", "b0bb24f", "1d85a4f"},
{"bugfix", "", GIT_SHORT_EMPTY_SHA}, {"bugfix", "", gitShortEmptyID},
}, },
}, },
} }
for _, test := range tests {
for _, tc := range testCases { t.Run("", func(t *testing.T) {
results := parseRemoteUpdateOutput(tc.output) assert.Equal(t, test.expResults, parseRemoteUpdateOutput(test.output))
So(len(results), ShouldEqual, len(tc.results))
for i := range tc.results {
So(tc.results[i].refName, ShouldEqual, results[i].refName)
So(tc.results[i].oldCommitID, ShouldEqual, results[i].oldCommitID)
So(tc.results[i].newCommitID, ShouldEqual, results[i].newCommitID)
}
}
}) })
} }
func Test_findPasswordInMirrorAddress(t *testing.T) {
Convey("Find password portion in mirror address", t, func() {
testCases := []struct {
addr string
start, end int
found bool
password string
}{
{"http://localhost:3000/user/repo.git", -1, -1, false, ""},
{"http://user@localhost:3000/user/repo.git", -1, -1, false, ""},
{"http://user:@localhost:3000/user/repo.git", -1, -1, false, ""},
{"http://user:password@localhost:3000/user/repo.git", 12, 20, true, "password"},
{"http://username:my%3Asecure%3Bpassword@localhost:3000/user/repo.git", 16, 38, true, "my%3Asecure%3Bpassword"},
{"http://username:my%40secure%23password@localhost:3000/user/repo.git", 16, 38, true, "my%40secure%23password"},
{"http://username:@@localhost:3000/user/repo.git", 16, 17, true, "@"},
}
for _, tc := range testCases {
start, end, found := findPasswordInMirrorAddress(tc.addr)
So(start, ShouldEqual, tc.start)
So(end, ShouldEqual, tc.end)
So(found, ShouldEqual, tc.found)
if found {
So(tc.addr[start:end], ShouldEqual, tc.password)
}
}
})
}
func Test_unescapeMirrorCredentials(t *testing.T) {
Convey("Escape credentials in mirror address", t, func() {
testCases := []string{
"http://localhost:3000/user/repo.git", "http://localhost:3000/user/repo.git",
"http://user@localhost:3000/user/repo.git", "http://user@localhost:3000/user/repo.git",
"http://user:@localhost:3000/user/repo.git", "http://user:@localhost:3000/user/repo.git",
"http://user:password@localhost:3000/user/repo.git", "http://user:password@localhost:3000/user/repo.git",
"http://user:my%3Asecure%3Bpassword@localhost:3000/user/repo.git", "http://user:my:secure;password@localhost:3000/user/repo.git",
"http://user:my%40secure%23password@localhost:3000/user/repo.git", "http://user:my@secure#password@localhost:3000/user/repo.git",
}
for i := 0; i < len(testCases); i += 2 {
So(unescapeMirrorCredentials(testCases[i]), ShouldEqual, testCases[i+1])
}
})
}
func Test_escapeMirrorCredentials(t *testing.T) {
Convey("Escape credentials in mirror address", t, func() {
testCases := []string{
"http://localhost:3000/user/repo.git", "http://localhost:3000/user/repo.git",
"http://user@localhost:3000/user/repo.git", "http://user@localhost:3000/user/repo.git",
"http://user:@localhost:3000/user/repo.git", "http://user:@localhost:3000/user/repo.git",
"http://user:password@localhost:3000/user/repo.git", "http://user:password@localhost:3000/user/repo.git",
"http://user:my:secure;password@localhost:3000/user/repo.git", "http://user:my%3Asecure%3Bpassword@localhost:3000/user/repo.git",
"http://user:my@secure#password@localhost:3000/user/repo.git", "http://user:my%40secure%23password@localhost:3000/user/repo.git",
}
for i := 0; i < len(testCases); i += 2 {
So(escapeMirrorCredentials(testCases[i]), ShouldEqual, testCases[i+1])
}
})
} }

View File

@@ -7,7 +7,6 @@ package db
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@@ -212,9 +211,9 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
} }
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
headGitRepo, err := git.OpenRepository(headRepoPath) headGitRepo, err := git.Open(headRepoPath)
if err != nil { if err != nil {
return fmt.Errorf("OpenRepository: %v", err) return fmt.Errorf("open repository: %v", err)
} }
// Create temporary directory to store temporary copy of the base repository, // Create temporary directory to store temporary copy of the base repository,
@@ -228,7 +227,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
var stderr string var stderr string
if _, stderr, err = process.ExecTimeout(5*time.Minute, if _, stderr, err = process.ExecTimeout(5*time.Minute,
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
"git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path, tmpBasePath); err != nil { "git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path(), tmpBasePath); err != nil {
return fmt.Errorf("git clone: %s", stderr) return fmt.Errorf("git clone: %s", stderr)
} }
@@ -311,13 +310,13 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
// Push changes on base branch to upstream. // Push changes on base branch to upstream.
if _, stderr, err = process.ExecDir(-1, tmpBasePath, if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
"git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { "git", "push", baseGitRepo.Path(), pr.BaseBranch); err != nil {
return fmt.Errorf("git push: %s", stderr) return fmt.Errorf("git push: %s", stderr)
} }
pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch) pr.MergedCommitID, err = headGitRepo.BranchCommitID(pr.HeadBranch)
if err != nil { if err != nil {
return fmt.Errorf("GetBranchCommit: %v", err) return fmt.Errorf("get head branch %q commit ID: %v", pr.HeadBranch, err)
} }
pr.HasMerged = true pr.HasMerged = true
@@ -351,42 +350,42 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
return nil return nil
} }
l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase) commits, err := headGitRepo.RevList([]string{pr.MergeBase + "..." + pr.MergedCommitID})
if err != nil { if err != nil {
log.Error("CommitsBetweenIDs: %v", err) log.Error("Failed to list commits [merge_base: %s, merged_commit_id: %s]: %v", pr.MergeBase, pr.MergedCommitID, err)
return nil return nil
} }
// It is possible that head branch is not fully sync with base branch for merge commits, // NOTE: It is possible that head branch is not fully sync with base branch
// so we need to get latest head commit and append merge commit manully // for merge commits, so we need to get latest head commit and append merge
// to avoid strange diff commits produced. // commit manully to avoid strange diff commits produced.
mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch) mergeCommit, err := baseGitRepo.BranchCommit(pr.BaseBranch)
if err != nil { if err != nil {
log.Error("GetBranchCommit: %v", err) log.Error("Failed to get base branch %q commit: %v", pr.BaseBranch, err)
return nil return nil
} }
if mergeStyle == MERGE_STYLE_REGULAR { if mergeStyle == MERGE_STYLE_REGULAR {
l.PushFront(mergeCommit) commits = append([]*git.Commit{mergeCommit}, commits...)
} }
commits, err := ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL()) pcs, err := CommitsToPushCommits(commits).ToApiPayloadCommits(pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL())
if err != nil { if err != nil {
log.Error("ToApiPayloadCommits: %v", err) log.Error("Failed to convert to API payload commits: %v", err)
return nil return nil
} }
p := &api.PushPayload{ p := &api.PushPayload{
Ref: git.BRANCH_PREFIX + pr.BaseBranch, Ref: git.RefsHeads + pr.BaseBranch,
Before: pr.MergeBase, Before: pr.MergeBase,
After: mergeCommit.ID.String(), After: mergeCommit.ID.String(),
CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID), CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
Commits: commits, Commits: pcs,
Repo: pr.BaseRepo.APIFormat(nil), Repo: pr.BaseRepo.APIFormat(nil),
Pusher: pr.HeadRepo.MustOwner().APIFormat(), Pusher: pr.HeadRepo.MustOwner().APIFormat(),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
} }
if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil { if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil {
log.Error("PrepareWebhooks: %v", err) log.Error("Failed to prepare webhooks: %v", err)
return nil return nil
} }
return nil return nil
@@ -599,36 +598,42 @@ func (pr *PullRequest) UpdatePatch() (err error) {
return nil return nil
} }
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) headGitRepo, err := git.Open(pr.HeadRepo.RepoPath())
if err != nil { if err != nil {
return fmt.Errorf("OpenRepository: %v", err) return fmt.Errorf("open repository: %v", err)
} }
// Add a temporary remote. // Add a temporary remote.
tmpRemote := com.ToStr(time.Now().UnixNano()) tmpRemote := com.ToStr(time.Now().UnixNano())
if err = headGitRepo.AddRemote(tmpRemote, RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name), true); err != nil { baseRepoPath := RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name)
return fmt.Errorf("AddRemote: %v", err) err = headGitRepo.AddRemote(tmpRemote, baseRepoPath, git.AddRemoteOptions{Fetch: true})
if err != nil {
return fmt.Errorf("add remote %q [repo_id: %d]: %v", tmpRemote, pr.HeadRepoID, err)
} }
defer func() { defer func() {
headGitRepo.RemoveRemote(tmpRemote) if err := headGitRepo.RemoveRemote(tmpRemote); err != nil {
log.Error("Failed to remove remote %q [repo_id: %d]: %v", tmpRemote, pr.HeadRepoID, err)
}
}() }()
remoteBranch := "remotes/" + tmpRemote + "/" + pr.BaseBranch remoteBranch := "remotes/" + tmpRemote + "/" + pr.BaseBranch
pr.MergeBase, err = headGitRepo.GetMergeBase(remoteBranch, pr.HeadBranch) pr.MergeBase, err = headGitRepo.MergeBase(remoteBranch, pr.HeadBranch)
if err != nil { if err != nil {
return fmt.Errorf("GetMergeBase: %v", err) return fmt.Errorf("get merge base: %v", err)
} else if err = pr.Update(); err != nil { } else if err = pr.Update(); err != nil {
return fmt.Errorf("Update: %v", err) return fmt.Errorf("update: %v", err)
} }
patch, err := headGitRepo.GetPatch(pr.MergeBase, pr.HeadBranch) patch, err := headGitRepo.DiffBinary(pr.MergeBase, pr.HeadBranch)
if err != nil { if err != nil {
return fmt.Errorf("GetPatch: %v", err) return fmt.Errorf("get binary patch: %v", err)
} }
if err = pr.BaseRepo.SavePatch(pr.Index, patch); err != nil { if err = pr.BaseRepo.SavePatch(pr.Index, patch); err != nil {
return fmt.Errorf("BaseRepo.SavePatch: %v", err) return fmt.Errorf("save patch: %v", err)
} }
log.Trace("PullRequest[%d].UpdatePatch: patch saved", pr.ID)
return nil return nil
} }
@@ -639,25 +644,35 @@ func (pr *PullRequest) PushToBaseRepo() (err error) {
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo 'refs/pull/%d/head'", pr.BaseRepoID, pr.Index) log.Trace("PushToBaseRepo[%d]: pushing commits to base repo 'refs/pull/%d/head'", pr.BaseRepoID, pr.Index)
headRepoPath := pr.HeadRepo.RepoPath() headRepoPath := pr.HeadRepo.RepoPath()
headGitRepo, err := git.OpenRepository(headRepoPath) headGitRepo, err := git.Open(headRepoPath)
if err != nil { if err != nil {
return fmt.Errorf("OpenRepository: %v", err) return fmt.Errorf("open repository: %v", err)
} }
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID) tmpRemote := fmt.Sprintf("tmp-pull-%d", pr.ID)
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil { if err = headGitRepo.AddRemote(tmpRemote, pr.BaseRepo.RepoPath()); err != nil {
return fmt.Errorf("headGitRepo.AddRemote: %v", err) return fmt.Errorf("add remote %q [repo_id: %d]: %v", tmpRemote, pr.HeadRepoID, err)
} }
// Make sure to remove the remote even if the push fails // Make sure to remove the remote even if the push fails
defer headGitRepo.RemoveRemote(tmpRemoteName) defer func() {
if err := headGitRepo.RemoveRemote(tmpRemote); err != nil {
log.Error("Failed to remove remote %q [repo_id: %d]: %v", tmpRemote, pr.HeadRepoID, err)
}
}()
headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index) headRefspec := fmt.Sprintf("refs/pull/%d/head", pr.Index)
headFile := filepath.Join(pr.BaseRepo.RepoPath(), headRefspec)
if osutil.IsExist(headFile) {
err = os.Remove(headFile)
if err != nil {
return fmt.Errorf("remove head file [repo_id: %d]: %v", pr.BaseRepoID, err)
}
}
// Remove head in case there is a conflict. err = headGitRepo.Push(tmpRemote, fmt.Sprintf("%s:%s", pr.HeadBranch, headRefspec))
os.Remove(path.Join(pr.BaseRepo.RepoPath(), headFile)) if err != nil {
return fmt.Errorf("push: %v", err)
if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil {
return fmt.Errorf("Push: %v", err)
} }
return nil return nil

View File

@@ -119,10 +119,10 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) {
func createTag(gitRepo *git.Repository, r *Release) error { func createTag(gitRepo *git.Repository, r *Release) error {
// Only actual create when publish. // Only actual create when publish.
if !r.IsDraft { if !r.IsDraft {
if !gitRepo.IsTagExist(r.TagName) { if !gitRepo.HasTag(r.TagName) {
commit, err := gitRepo.GetBranchCommit(r.Target) commit, err := gitRepo.BranchCommit(r.Target)
if err != nil { if err != nil {
return fmt.Errorf("GetBranchCommit: %v", err) return fmt.Errorf("get branch commit: %v", err)
} }
// Trim '--' prefix to prevent command line argument vulnerability. // Trim '--' prefix to prevent command line argument vulnerability.
@@ -134,15 +134,15 @@ func createTag(gitRepo *git.Repository, r *Release) error {
return err return err
} }
} else { } else {
commit, err := gitRepo.GetTagCommit(r.TagName) commit, err := gitRepo.TagCommit(r.TagName)
if err != nil { if err != nil {
return fmt.Errorf("GetTagCommit: %v", err) return fmt.Errorf("get tag commit: %v", err)
} }
r.Sha1 = commit.ID.String() r.Sha1 = commit.ID.String()
r.NumCommits, err = commit.CommitsCount() r.NumCommits, err = commit.CommitsCount()
if err != nil { if err != nil {
return fmt.Errorf("CommitsCount: %v", err) return fmt.Errorf("count commits: %v", err)
} }
} }
} }

View File

@@ -119,9 +119,6 @@ func NewRepoContext() {
if version.Compare("1.8.3", conf.Git.Version, ">") { if version.Compare("1.8.3", conf.Git.Version, ">") {
log.Fatal("Gogs requires Git version greater or equal to 1.8.3") log.Fatal("Gogs requires Git version greater or equal to 1.8.3")
} }
git.HookDir = "custom_hooks"
git.HookSampleDir = "hooks"
git.DefaultCommitsPageSize = conf.UI.User.CommitsPagingNum
// Git requires setting user.name and user.email in order to commit changes. // Git requires setting user.name and user.email in order to commit changes.
for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "gogs@fake.local"} { for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "gogs@fake.local"} {
@@ -420,9 +417,9 @@ func (repo *Repository) mustOwner(e Engine) *User {
} }
func (repo *Repository) UpdateSize() error { func (repo *Repository) UpdateSize() error {
countObject, err := git.GetRepoSize(repo.RepoPath()) countObject, err := git.RepoCountObjects(repo.RepoPath())
if err != nil { if err != nil {
return fmt.Errorf("GetRepoSize: %v", err) return fmt.Errorf("count repository objects: %v", err)
} }
repo.Size = countObject.Size + countObject.SizePack repo.Size = countObject.Size + countObject.SizePack
@@ -602,33 +599,41 @@ func (repo *Repository) LocalCopyPath() string {
// assume subsequent operations are against target branch when caller has confidence // assume subsequent operations are against target branch when caller has confidence
// about no race condition. // about no race condition.
func UpdateLocalCopyBranch(repoPath, localPath, branch string, isWiki bool) (err error) { func UpdateLocalCopyBranch(repoPath, localPath, branch string, isWiki bool) (err error) {
if !com.IsExist(localPath) { if !osutil.IsExist(localPath) {
// Checkout to a specific branch fails when wiki is an empty repository. // Checkout to a specific branch fails when wiki is an empty repository.
if isWiki { if isWiki {
branch = "" branch = ""
} }
if err = git.Clone(repoPath, localPath, git.CloneRepoOptions{ if err = git.Clone(repoPath, localPath, git.CloneOptions{
Timeout: time.Duration(conf.Git.Timeout.Clone) * time.Second,
Branch: branch, Branch: branch,
Timeout: time.Duration(conf.Git.Timeout.Clone) * time.Second,
}); err != nil { }); err != nil {
return fmt.Errorf("git clone %s: %v", branch, err) return fmt.Errorf("git clone [branch: %s]: %v", branch, err)
} }
} else { return nil
if err = git.Fetch(localPath, git.FetchRemoteOptions{ }
gitRepo, err := git.Open(localPath)
if err != nil {
return fmt.Errorf("open repository: %v", err)
}
if err = gitRepo.Fetch(git.FetchOptions{
Prune: true, Prune: true,
}); err != nil { }); err != nil {
return fmt.Errorf("git fetch: %v", err) return fmt.Errorf("fetch: %v", err)
} }
if err = git.Checkout(localPath, git.CheckoutOptions{
Branch: branch, if err = gitRepo.Checkout(branch); err != nil {
}); err != nil { return fmt.Errorf("checkout [branch: %s]: %v", branch, err)
return fmt.Errorf("git checkout %s: %v", branch, err)
} }
// Reset to align with remote in case of force push. // Reset to align with remote in case of force push.
if err = git.ResetHEAD(localPath, true, "origin/"+branch); err != nil { rev := "origin/" + branch
return fmt.Errorf("git reset --hard origin/%s: %v", branch, err) if err = gitRepo.Reset(rev, git.ResetOptions{
} Hard: true,
}); err != nil {
return fmt.Errorf("reset [revision: %s]: %v", rev, err)
} }
return nil return nil
} }
@@ -729,9 +734,7 @@ func wikiRemoteURL(remote string) string {
remote = strings.TrimSuffix(remote, ".git") remote = strings.TrimSuffix(remote, ".git")
for _, suffix := range commonWikiURLSuffixes { for _, suffix := range commonWikiURLSuffixes {
wikiURL := remote + suffix wikiURL := remote + suffix
if git.IsRepoURLAccessible(git.NetworkOptions{ if git.IsURLAccessible(time.Minute, wikiURL) {
URL: wikiURL,
}) {
return wikiURL return wikiURL
} }
} }
@@ -766,23 +769,23 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
migrateTimeout := time.Duration(conf.Git.Timeout.Migrate) * time.Second migrateTimeout := time.Duration(conf.Git.Timeout.Migrate) * time.Second
RemoveAllWithNotice("Repository path erase before creation", repoPath) RemoveAllWithNotice("Repository path erase before creation", repoPath)
if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{ if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneOptions{
Mirror: true, Mirror: true,
Quiet: true, Quiet: true,
Timeout: migrateTimeout, Timeout: migrateTimeout,
}); err != nil { }); err != nil {
return repo, fmt.Errorf("Clone: %v", err) return repo, fmt.Errorf("clone: %v", err)
} }
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr) wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
if len(wikiRemotePath) > 0 { if len(wikiRemotePath) > 0 {
RemoveAllWithNotice("Repository wiki path erase before creation", wikiPath) RemoveAllWithNotice("Repository wiki path erase before creation", wikiPath)
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{ if err = git.Clone(wikiRemotePath, wikiPath, git.CloneOptions{
Mirror: true, Mirror: true,
Quiet: true, Quiet: true,
Timeout: migrateTimeout, Timeout: migrateTimeout,
}); err != nil { }); err != nil {
log.Trace("Failed to clone wiki: %v", err) log.Error("Failed to clone wiki: %v", err)
RemoveAllWithNotice("Delete repository wiki for initialization failure", wikiPath) RemoveAllWithNotice("Delete repository wiki for initialization failure", wikiPath)
} }
} }
@@ -799,17 +802,15 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
if !repo.IsBare { if !repo.IsBare {
// Try to get HEAD branch and set it as default branch. // Try to get HEAD branch and set it as default branch.
gitRepo, err := git.OpenRepository(repoPath) gitRepo, err := git.Open(repoPath)
if err != nil { if err != nil {
return repo, fmt.Errorf("OpenRepository: %v", err) return repo, fmt.Errorf("open repository: %v", err)
} }
headBranch, err := gitRepo.GetHEADBranch() refspec, err := gitRepo.SymbolicRef()
if err != nil { if err != nil {
return repo, fmt.Errorf("GetHEADBranch: %v", err) return repo, fmt.Errorf("get HEAD branch: %v", err)
}
if headBranch != nil {
repo.DefaultBranch = headBranch.Name
} }
repo.DefaultBranch = git.RefShortName(refspec)
if err = repo.UpdateSize(); err != nil { if err = repo.UpdateSize(); err != nil {
log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err) log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err)
@@ -847,15 +848,15 @@ func cleanUpMigrateGitConfig(configPath string) error {
return nil return nil
} }
var hooksTpls = map[string]string{ var hooksTpls = map[git.HookName]string{
"pre-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", "pre-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n",
"update": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", "update": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n",
"post-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", "post-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n",
} }
func createDelegateHooks(repoPath string) (err error) { func createDelegateHooks(repoPath string) (err error) {
for _, name := range git.HookNames { for _, name := range git.ServerSideHooks {
hookPath := filepath.Join(repoPath, "hooks", name) hookPath := filepath.Join(repoPath, "hooks", string(name))
if err = ioutil.WriteFile(hookPath, if err = ioutil.WriteFile(hookPath,
[]byte(fmt.Sprintf(hooksTpls[name], conf.Repository.ScriptType, conf.AppPath(), conf.CustomConf)), []byte(fmt.Sprintf(hooksTpls[name], conf.Repository.ScriptType, conf.AppPath(), conf.CustomConf)),
os.ModePerm); err != nil { os.ModePerm); err != nil {
@@ -1005,8 +1006,8 @@ func initRepository(e Engine, repoPath string, doer *User, repo *Repository, opt
} }
// Init bare new repository. // Init bare new repository.
if err = git.InitRepository(repoPath, true); err != nil { if err = git.Init(repoPath, git.InitOptions{Bare: true}); err != nil {
return fmt.Errorf("InitRepository: %v", err) return fmt.Errorf("init repository: %v", err)
} else if err = createDelegateHooks(repoPath); err != nil { } else if err = createDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err) return fmt.Errorf("createDelegateHooks: %v", err)
} }
@@ -1880,9 +1881,9 @@ func ReinitMissingRepositories() error {
for _, repo := range repos { for _, repo := range repos {
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
if err := git.InitRepository(repo.RepoPath(), true); err != nil { if err := git.Init(repo.RepoPath(), git.InitOptions{Bare: true}); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("InitRepository [%d]: %v", repo.ID, err)); err2 != nil { if err2 := CreateRepositoryNotice(fmt.Sprintf("init repository [repo_id: %d]: %v", repo.ID, err)); err2 != nil {
return fmt.Errorf("CreateRepositoryNotice: %v", err) return fmt.Errorf("create repository notice: %v", err)
} }
} }
} }
@@ -1930,7 +1931,11 @@ func GitFsck() {
func(idx int, bean interface{}) error { func(idx int, bean interface{}) error {
repo := bean.(*Repository) repo := bean.(*Repository)
repoPath := repo.RepoPath() repoPath := repo.RepoPath()
if err := git.Fsck(repoPath, conf.Cron.RepoHealthCheck.Timeout, conf.Cron.RepoHealthCheck.Args...); err != nil { err := git.RepoFsck(repoPath, git.FsckOptions{
Args: conf.Cron.RepoHealthCheck.Args,
Timeout: conf.Cron.RepoHealthCheck.Timeout,
})
if err != nil {
desc := fmt.Sprintf("Failed to perform health check on repository '%s': %v", repoPath, err) desc := fmt.Sprintf("Failed to perform health check on repository '%s': %v", repoPath, err)
log.Warn(desc) log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil { if err = CreateRepositoryNotice(desc); err != nil {
@@ -2441,24 +2446,24 @@ func (repo *Repository) GetForks() ([]*Repository, error) {
// \/ \/ \/ \/ \/ // \/ \/ \/ \/ \/
// //
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) { func (repo *Repository) CreateNewBranch(oldBranch, newBranch string) (err error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID)) repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
localPath := repo.LocalCopyPath() localPath := repo.LocalCopyPath()
if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil { if err = discardLocalRepoBranchChanges(localPath, oldBranch); err != nil {
return fmt.Errorf("discardLocalRepoChanges: %v", err) return fmt.Errorf("discard changes in local copy [path: %s, branch: %s]: %v", localPath, oldBranch, err)
} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil { } else if err = repo.UpdateLocalCopyBranch(oldBranch); err != nil {
return fmt.Errorf("UpdateLocalCopyBranch: %v", err) return fmt.Errorf("update branch for local copy [path: %s, branch: %s]: %v", localPath, oldBranch, err)
} }
if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil { if err = repo.CheckoutNewBranch(oldBranch, newBranch); err != nil {
return fmt.Errorf("CreateNewBranch: %v", err) return fmt.Errorf("create new branch [base: %s, new: %s]: %v", oldBranch, newBranch, err)
} }
if err = git.Push(localPath, "origin", branchName); err != nil { if err = git.RepoPush(localPath, "origin", newBranch); err != nil {
return fmt.Errorf("Push: %v", err) return fmt.Errorf("push [branch: %s]: %v", newBranch, err)
} }
return nil return nil

View File

@@ -24,33 +24,33 @@ type Branch struct {
} }
func GetBranchesByPath(path string) ([]*Branch, error) { func GetBranchesByPath(path string) ([]*Branch, error) {
gitRepo, err := git.OpenRepository(path) gitRepo, err := git.Open(path)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("open repository: %v", err)
} }
brs, err := gitRepo.GetBranches() names, err := gitRepo.Branches()
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("list branches")
} }
branches := make([]*Branch, len(brs)) branches := make([]*Branch, len(names))
for i := range brs { for i := range names {
branches[i] = &Branch{ branches[i] = &Branch{
RepoPath: path, RepoPath: path,
Name: brs[i], Name: names[i],
} }
} }
return branches, nil return branches, nil
} }
func (repo *Repository) GetBranch(br string) (*Branch, error) { func (repo *Repository) GetBranch(name string) (*Branch, error) {
if !git.IsBranchExist(repo.RepoPath(), br) { if !git.RepoHasBranch(repo.RepoPath(), name) {
return nil, errors.ErrBranchNotExist{Name: br} return nil, errors.ErrBranchNotExist{Name: name}
} }
return &Branch{ return &Branch{
RepoPath: repo.RepoPath(), RepoPath: repo.RepoPath(),
Name: br, Name: name,
}, nil }, nil
} }
@@ -59,11 +59,11 @@ func (repo *Repository) GetBranches() ([]*Branch, error) {
} }
func (br *Branch) GetCommit() (*git.Commit, error) { func (br *Branch) GetCommit() (*git.Commit, error) {
gitRepo, err := git.OpenRepository(br.RepoPath) gitRepo, err := git.Open(br.RepoPath)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("open repository: %v", err)
} }
return gitRepo.GetBranchCommit(br.Name) return gitRepo.BranchCommit(br.Name)
} }
type ProtectBranchWhitelist struct { type ProtectBranchWhitelist struct {

View File

@@ -23,6 +23,7 @@ import (
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/process" "gogs.io/gogs/internal/process"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
@@ -58,7 +59,7 @@ func ComposeHookEnvs(opts ComposeHookEnvsOptions) []string {
ENV_REPO_OWNER_SALT_MD5 + "=" + tool.MD5(opts.OwnerSalt), ENV_REPO_OWNER_SALT_MD5 + "=" + tool.MD5(opts.OwnerSalt),
ENV_REPO_ID + "=" + com.ToStr(opts.RepoID), ENV_REPO_ID + "=" + com.ToStr(opts.RepoID),
ENV_REPO_NAME + "=" + opts.RepoName, ENV_REPO_NAME + "=" + opts.RepoName,
ENV_REPO_CUSTOM_HOOKS_PATH + "=" + path.Join(opts.RepoPath, "custom_hooks"), ENV_REPO_CUSTOM_HOOKS_PATH + "=" + filepath.Join(opts.RepoPath, "custom_hooks"),
} }
return envs return envs
} }
@@ -76,14 +77,15 @@ func discardLocalRepoBranchChanges(localPath, branch string) error {
if !com.IsExist(localPath) { if !com.IsExist(localPath) {
return nil return nil
} }
// No need to check if nothing in the repository. // No need to check if nothing in the repository.
if !git.IsBranchExist(localPath, branch) { if !git.RepoHasBranch(localPath, branch) {
return nil return nil
} }
refName := "origin/" + branch rev := "origin/" + branch
if err := git.ResetHEAD(localPath, true, refName); err != nil { if err := git.RepoReset(localPath, rev, git.ResetOptions{Hard: true}); err != nil {
return fmt.Errorf("git reset --hard %s: %v", refName, err) return fmt.Errorf("reset [revision: %s]: %v", rev, err)
} }
return nil return nil
} }
@@ -92,22 +94,17 @@ func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
} }
// checkoutNewBranch checks out to a new branch from the a branch name. // CheckoutNewBranch checks out to a new branch from the a branch name.
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
if err := git.Checkout(localPath, git.CheckoutOptions{ if err := git.RepoCheckout(repo.LocalCopyPath(), newBranch, git.CheckoutOptions{
BaseBranch: oldBranch,
Timeout: time.Duration(conf.Git.Timeout.Pull) * time.Second, Timeout: time.Duration(conf.Git.Timeout.Pull) * time.Second,
Branch: newBranch,
OldBranch: oldBranch,
}); err != nil { }); err != nil {
return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err) return fmt.Errorf("checkout [base: %s, new: %s]: %v", oldBranch, newBranch, err)
} }
return nil return nil
} }
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
}
type UpdateRepoFileOptions struct { type UpdateRepoFileOptions struct {
LastCommitID string LastCommitID string
OldBranch string OldBranch string
@@ -135,16 +132,16 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
if opts.OldBranch != opts.NewBranch { if opts.OldBranch != opts.NewBranch {
// Directly return error if new branch already exists in the server // Directly return error if new branch already exists in the server
if git.IsBranchExist(repoPath, opts.NewBranch) { if git.RepoHasBranch(repoPath, opts.NewBranch) {
return errors.BranchAlreadyExists{Name: opts.NewBranch} return errors.BranchAlreadyExists{Name: opts.NewBranch}
} }
// Otherwise, delete branch from local copy in case out of sync // Otherwise, delete branch from local copy in case out of sync
if git.IsBranchExist(localPath, opts.NewBranch) { if git.RepoHasBranch(localPath, opts.NewBranch) {
if err = git.DeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{ if err = git.RepoDeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{
Force: true, Force: true,
}); err != nil { }); err != nil {
return fmt.Errorf("delete branch[%s]: %v", opts.NewBranch, err) return fmt.Errorf("delete branch %q: %v", opts.NewBranch, err)
} }
} }
@@ -167,7 +164,7 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
// Ignore move step if it's a new file under a directory. // Ignore move step if it's a new file under a directory.
// Otherwise, move the file when name changed. // Otherwise, move the file when name changed.
if osutil.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName { if osutil.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName {
if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil { if err = git.RepoMove(localPath, opts.OldTreeName, opts.NewTreeName); err != nil {
return fmt.Errorf("git mv %q %q: %v", opts.OldTreeName, opts.NewTreeName, err) return fmt.Errorf("git mv %q %q: %v", opts.OldTreeName, opts.NewTreeName, err)
} }
} }
@@ -176,29 +173,28 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
return fmt.Errorf("write file: %v", err) return fmt.Errorf("write file: %v", err)
} }
if err = git.AddChanges(localPath, true); err != nil { if err = git.RepoAdd(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("git add --all: %v", err) return fmt.Errorf("git add --all: %v", err)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ } else if err = git.RepoCommit(localPath, doer.NewGitSig(), opts.Message); err != nil {
Committer: doer.NewGitSig(),
Message: opts.Message,
}); err != nil {
return fmt.Errorf("commit changes on %q: %v", localPath, err) return fmt.Errorf("commit changes on %q: %v", localPath, err)
} else if err = git.PushWithEnvs(localPath, "origin", opts.NewBranch, }
ComposeHookEnvs(ComposeHookEnvsOptions{
envs := ComposeHookEnvs(ComposeHookEnvsOptions{
AuthUser: doer, AuthUser: doer,
OwnerName: repo.MustOwner().Name, OwnerName: repo.MustOwner().Name,
OwnerSalt: repo.MustOwner().Salt, OwnerSalt: repo.MustOwner().Salt,
RepoID: repo.ID, RepoID: repo.ID,
RepoName: repo.Name, RepoName: repo.Name,
RepoPath: repo.RepoPath(), RepoPath: repo.RepoPath(),
})); err != nil { })
if err = git.RepoPush(localPath, "origin", opts.NewBranch, git.PushOptions{Envs: envs}); err != nil {
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
} }
return nil return nil
} }
// GetDiffPreview produces and returns diff result of a file which is not yet committed. // GetDiffPreview produces and returns diff result of a file which is not yet committed.
func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *Diff, err error) { func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *gitutil.Diff, err error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID)) repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
@@ -231,9 +227,9 @@ func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *
pid := process.Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd) pid := process.Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd)
defer process.Remove(pid) defer process.Remove(pid)
diff, err = ParsePatch(conf.Git.MaxGitDiffLines, conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles, stdout) diff, err = gitutil.ParseDiff(stdout, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars)
if err != nil { if err != nil {
return nil, fmt.Errorf("parse path: %v", err) return nil, fmt.Errorf("parse diff: %v", err)
} }
if err = cmd.Wait(); err != nil { if err = cmd.Wait(); err != nil {
@@ -280,22 +276,21 @@ func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (
return fmt.Errorf("remove file %q: %v", opts.TreePath, err) return fmt.Errorf("remove file %q: %v", opts.TreePath, err)
} }
if err = git.AddChanges(localPath, true); err != nil { if err = git.RepoAdd(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("git add --all: %v", err) return fmt.Errorf("git add --all: %v", err)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ } else if err = git.RepoCommit(localPath, doer.NewGitSig(), opts.Message); err != nil {
Committer: doer.NewGitSig(),
Message: opts.Message,
}); err != nil {
return fmt.Errorf("commit changes to %q: %v", localPath, err) return fmt.Errorf("commit changes to %q: %v", localPath, err)
} else if err = git.PushWithEnvs(localPath, "origin", opts.NewBranch, }
ComposeHookEnvs(ComposeHookEnvsOptions{
envs := ComposeHookEnvs(ComposeHookEnvsOptions{
AuthUser: doer, AuthUser: doer,
OwnerName: repo.MustOwner().Name, OwnerName: repo.MustOwner().Name,
OwnerSalt: repo.MustOwner().Salt, OwnerSalt: repo.MustOwner().Salt,
RepoID: repo.ID, RepoID: repo.ID,
RepoName: repo.Name, RepoName: repo.Name,
RepoPath: repo.RepoPath(), RepoPath: repo.RepoPath(),
})); err != nil { })
if err = git.RepoPush(localPath, "origin", opts.NewBranch, git.PushOptions{Envs: envs}); err != nil {
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
} }
return nil return nil
@@ -496,22 +491,21 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
} }
} }
if err = git.AddChanges(localPath, true); err != nil { if err = git.RepoAdd(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("git add --all: %v", err) return fmt.Errorf("git add --all: %v", err)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ } else if err = git.RepoCommit(localPath, doer.NewGitSig(), opts.Message); err != nil {
Committer: doer.NewGitSig(),
Message: opts.Message,
}); err != nil {
return fmt.Errorf("commit changes on %q: %v", localPath, err) return fmt.Errorf("commit changes on %q: %v", localPath, err)
} else if err = git.PushWithEnvs(localPath, "origin", opts.NewBranch, }
ComposeHookEnvs(ComposeHookEnvsOptions{
envs := ComposeHookEnvs(ComposeHookEnvsOptions{
AuthUser: doer, AuthUser: doer,
OwnerName: repo.MustOwner().Name, OwnerName: repo.MustOwner().Name,
OwnerSalt: repo.MustOwner().Salt, OwnerSalt: repo.MustOwner().Salt,
RepoID: repo.ID, RepoID: repo.ID,
RepoName: repo.Name, RepoName: repo.Name,
RepoPath: repo.RepoPath(), RepoPath: repo.RepoPath(),
})); err != nil { })
if err = git.RepoPush(localPath, "origin", opts.NewBranch, git.PushOptions{Envs: envs}); err != nil {
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
} }

View File

@@ -5,19 +5,18 @@
package db package db
import ( import (
"container/list"
"fmt" "fmt"
"os/exec" "os/exec"
"strings" "strings"
git "github.com/gogs/git-module" "github.com/gogs/git-module"
) )
// CommitToPushCommit transforms a git.Commit to PushCommit type. // CommitToPushCommit transforms a git.Commit to PushCommit type.
func CommitToPushCommit(commit *git.Commit) *PushCommit { func CommitToPushCommit(commit *git.Commit) *PushCommit {
return &PushCommit{ return &PushCommit{
Sha1: commit.ID.String(), Sha1: commit.ID.String(),
Message: commit.Message(), Message: commit.Message,
AuthorEmail: commit.Author.Email, AuthorEmail: commit.Author.Email,
AuthorName: commit.Author.Name, AuthorName: commit.Author.Name,
CommitterEmail: commit.Committer.Email, CommitterEmail: commit.Committer.Email,
@@ -26,27 +25,22 @@ func CommitToPushCommit(commit *git.Commit) *PushCommit {
} }
} }
func ListToPushCommits(l *list.List) *PushCommits { func CommitsToPushCommits(commits []*git.Commit) *PushCommits {
if l == nil { if len(commits) == 0 {
return &PushCommits{} return &PushCommits{}
} }
commits := make([]*PushCommit, 0) pcs := make([]*PushCommit, len(commits))
var actEmail string for i := range commits {
for e := l.Front(); e != nil; e = e.Next() { pcs[i] = CommitToPushCommit(commits[i])
commit := e.Value.(*git.Commit)
if actEmail == "" {
actEmail = commit.Committer.Email
} }
commits = append(commits, CommitToPushCommit(commit)) return &PushCommits{len(pcs), pcs, "", nil}
}
return &PushCommits{l.Len(), commits, "", nil}
} }
type PushUpdateOptions struct { type PushUpdateOptions struct {
OldCommitID string OldCommitID string
NewCommitID string NewCommitID string
RefFullName string FullRefspec string
PusherID int64 PusherID int64
PusherName string PusherName string
RepoUserName string RepoUserName string
@@ -56,10 +50,10 @@ type PushUpdateOptions struct {
// PushUpdate must be called for any push actions in order to // PushUpdate must be called for any push actions in order to
// generates necessary push action history feeds. // generates necessary push action history feeds.
func PushUpdate(opts PushUpdateOptions) (err error) { func PushUpdate(opts PushUpdateOptions) (err error) {
isNewRef := opts.OldCommitID == git.EMPTY_SHA isNewRef := strings.HasPrefix(opts.OldCommitID, git.EmptyID)
isDelRef := opts.NewCommitID == git.EMPTY_SHA isDelRef := strings.HasPrefix(opts.NewCommitID, git.EmptyID)
if isNewRef && isDelRef { if isNewRef && isDelRef {
return fmt.Errorf("Old and new revisions are both %s", git.EMPTY_SHA) return fmt.Errorf("both old and new revisions are %q", git.EmptyID)
} }
repoPath := RepoPath(opts.RepoUserName, opts.RepoName) repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
@@ -70,9 +64,9 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
return fmt.Errorf("run 'git update-server-info': %v", err) return fmt.Errorf("run 'git update-server-info': %v", err)
} }
gitRepo, err := git.OpenRepository(repoPath) gitRepo, err := git.Open(repoPath)
if err != nil { if err != nil {
return fmt.Errorf("OpenRepository: %v", err) return fmt.Errorf("open repository: %v", err)
} }
owner, err := GetUserByName(opts.RepoUserName) owner, err := GetUserByName(opts.RepoUserName)
@@ -90,12 +84,12 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
} }
// Push tags // Push tags
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) { if strings.HasPrefix(opts.FullRefspec, git.RefsTags) {
if err := CommitRepoAction(CommitRepoActionOptions{ if err := CommitRepoAction(CommitRepoActionOptions{
PusherName: opts.PusherName, PusherName: opts.PusherName,
RepoOwnerID: owner.ID, RepoOwnerID: owner.ID,
RepoName: repo.Name, RepoName: repo.Name,
RefFullName: opts.RefFullName, RefFullName: opts.FullRefspec,
OldCommitID: opts.OldCommitID, OldCommitID: opts.OldCommitID,
NewCommitID: opts.NewCommitID, NewCommitID: opts.NewCommitID,
Commits: &PushCommits{}, Commits: &PushCommits{},
@@ -105,22 +99,23 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
return nil return nil
} }
var l *list.List var commits []*git.Commit
// Skip read parent commits when delete branch // Skip read parent commits when delete branch
if !isDelRef { if !isDelRef {
// Push new branch // Push new branch
newCommit, err := gitRepo.GetCommit(opts.NewCommitID) newCommit, err := gitRepo.CatFileCommit(opts.NewCommitID)
if err != nil { if err != nil {
return fmt.Errorf("GetCommit [commit_id: %s]: %v", opts.NewCommitID, err) return fmt.Errorf("GetCommit [commit_id: %s]: %v", opts.NewCommitID, err)
} }
if isNewRef { if isNewRef {
l, err = newCommit.CommitsBeforeLimit(10) commits, err = newCommit.Ancestors(git.LogOptions{MaxCount: 9})
if err != nil { if err != nil {
return fmt.Errorf("CommitsBeforeLimit [commit_id: %s]: %v", newCommit.ID, err) return fmt.Errorf("CommitsBeforeLimit [commit_id: %s]: %v", newCommit.ID, err)
} }
commits = append([]*git.Commit{newCommit}, commits...)
} else { } else {
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID) commits, err = newCommit.CommitsAfter(opts.OldCommitID)
if err != nil { if err != nil {
return fmt.Errorf("CommitsBeforeUntil [commit_id: %s]: %v", opts.OldCommitID, err) return fmt.Errorf("CommitsBeforeUntil [commit_id: %s]: %v", opts.OldCommitID, err)
} }
@@ -131,10 +126,10 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
PusherName: opts.PusherName, PusherName: opts.PusherName,
RepoOwnerID: owner.ID, RepoOwnerID: owner.ID,
RepoName: repo.Name, RepoName: repo.Name,
RefFullName: opts.RefFullName, RefFullName: opts.FullRefspec,
OldCommitID: opts.OldCommitID, OldCommitID: opts.OldCommitID,
NewCommitID: opts.NewCommitID, NewCommitID: opts.NewCommitID,
Commits: ListToPushCommits(l), Commits: CommitsToPushCommits(commits),
}); err != nil { }); err != nil {
return fmt.Errorf("CommitRepoAction.(branch): %v", err) return fmt.Errorf("CommitRepoAction.(branch): %v", err)
} }

View File

@@ -6,7 +6,6 @@ package db
import ( import (
"bytes" "bytes"
"container/list"
"crypto/sha256" "crypto/sha256"
"crypto/subtle" "crypto/subtle"
"encoding/hex" "encoding/hex"
@@ -977,28 +976,22 @@ func ValidateCommitWithEmail(c *git.Commit) *User {
} }
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users. // ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List { func ValidateCommitsWithEmails(oldCommits []*git.Commit) []*UserCommit {
var ( emails := make(map[string]*User)
u *User newCommits := make([]*UserCommit, len(oldCommits))
emails = map[string]*User{} for i := range oldCommits {
newCommits = list.New() var u *User
e = oldCommits.Front() if v, ok := emails[oldCommits[i].Author.Email]; !ok {
) u, _ = GetUserByEmail(oldCommits[i].Author.Email)
for e != nil { emails[oldCommits[i].Author.Email] = u
c := e.Value.(*git.Commit)
if v, ok := emails[c.Author.Email]; !ok {
u, _ = GetUserByEmail(c.Author.Email)
emails[c.Author.Email] = u
} else { } else {
u = v u = v
} }
newCommits.PushBack(UserCommit{ newCommits[i] = &UserCommit{
User: u, User: u,
Commit: c, Commit: oldCommits[i],
}) }
e = e.Next()
} }
return newCommits return newCommits
} }

View File

@@ -86,7 +86,7 @@ func GetDingtalkPayload(p api.Payloader, event HookEventType) (payload *Dingtalk
} }
func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) { func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
refType := strings.Title(p.RefType) refType := strings.Title(p.RefType)
actionCard := NewDingtalkActionCard("View "+refType, p.Repo.HTMLURL+"/src/"+refName) actionCard := NewDingtalkActionCard("View "+refType, p.Repo.HTMLURL+"/src/"+refName)
@@ -99,7 +99,7 @@ func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) {
} }
func getDingtalkDeletePayload(p *api.DeletePayload) (*DingtalkPayload, error) { func getDingtalkDeletePayload(p *api.DeletePayload) (*DingtalkPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
refType := strings.Title(p.RefType) refType := strings.Title(p.RefType)
actionCard := NewDingtalkActionCard("View Repo", p.Repo.HTMLURL) actionCard := NewDingtalkActionCard("View Repo", p.Repo.HTMLURL)
@@ -122,7 +122,7 @@ func getDingtalkForkPayload(p *api.ForkPayload) (*DingtalkPayload, error) {
} }
func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) { func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
pusher := p.Pusher.FullName pusher := p.Pusher.FullName
if pusher == "" { if pusher == "" {

View File

@@ -71,7 +71,7 @@ func DiscordSHALinkFormatter(url string, text string) string {
// getDiscordCreatePayload composes Discord payload for create new branch or tag. // getDiscordCreatePayload composes Discord payload for create new branch or tag.
func getDiscordCreatePayload(p *api.CreatePayload) (*DiscordPayload, error) { func getDiscordCreatePayload(p *api.CreatePayload) (*DiscordPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
content := fmt.Sprintf("Created new %s: %s/%s", p.RefType, repoLink, refLink) content := fmt.Sprintf("Created new %s: %s/%s", p.RefType, repoLink, refLink)
@@ -89,7 +89,7 @@ func getDiscordCreatePayload(p *api.CreatePayload) (*DiscordPayload, error) {
// getDiscordDeletePayload composes Discord payload for delete a branch or tag. // getDiscordDeletePayload composes Discord payload for delete a branch or tag.
func getDiscordDeletePayload(p *api.DeletePayload) (*DiscordPayload, error) { func getDiscordDeletePayload(p *api.DeletePayload) (*DiscordPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName) content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName)
return &DiscordPayload{ return &DiscordPayload{
@@ -124,7 +124,7 @@ func getDiscordForkPayload(p *api.ForkPayload) (*DiscordPayload, error) {
func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) (*DiscordPayload, error) { func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) (*DiscordPayload, error) {
// n new commits // n new commits
var ( var (
branchName = git.RefEndName(p.Ref) branchName = git.RefShortName(p.Ref)
commitDesc string commitDesc string
commitString string commitString string
) )

View File

@@ -72,7 +72,7 @@ func SlackLinkFormatter(url string, text string) string {
// getSlackCreatePayload composes Slack payload for create new branch or tag. // getSlackCreatePayload composes Slack payload for create new branch or tag.
func getSlackCreatePayload(p *api.CreatePayload) (*SlackPayload, error) { func getSlackCreatePayload(p *api.CreatePayload) (*SlackPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
@@ -83,7 +83,7 @@ func getSlackCreatePayload(p *api.CreatePayload) (*SlackPayload, error) {
// getSlackDeletePayload composes Slack payload for delete a branch or tag. // getSlackDeletePayload composes Slack payload for delete a branch or tag.
func getSlackDeletePayload(p *api.DeletePayload) (*SlackPayload, error) { func getSlackDeletePayload(p *api.DeletePayload) (*SlackPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName) text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
return &SlackPayload{ return &SlackPayload{
@@ -104,7 +104,7 @@ func getSlackForkPayload(p *api.ForkPayload) (*SlackPayload, error) {
func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
// n new commits // n new commits
var ( var (
branchName = git.RefEndName(p.Ref) branchName = git.RefShortName(p.Ref)
commitDesc string commitDesc string
commitString string commitString string
) )

View File

@@ -62,8 +62,8 @@ func (repo *Repository) InitWiki() error {
return nil return nil
} }
if err := git.InitRepository(repo.WikiPath(), true); err != nil { if err := git.Init(repo.WikiPath(), git.InitOptions{Bare: true}); err != nil {
return fmt.Errorf("InitRepository: %v", err) return fmt.Errorf("init repository: %v", err)
} else if err = createDelegateHooks(repo.WikiPath()); err != nil { } else if err = createDelegateHooks(repo.WikiPath()); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err) return fmt.Errorf("createDelegateHooks: %v", err)
} }
@@ -125,15 +125,12 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
if len(message) == 0 { if len(message) == 0 {
message = "Update page '" + title + "'" message = "Update page '" + title + "'"
} }
if err = git.AddChanges(localPath, true); err != nil { if err = git.RepoAdd(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("AddChanges: %v", err) return fmt.Errorf("add all changes: %v", err)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ } else if err = git.RepoCommit(localPath, doer.NewGitSig(), message); err != nil {
Committer: doer.NewGitSig(), return fmt.Errorf("commit changes: %v", err)
Message: message, } else if err = git.RepoPush(localPath, "origin", "master"); err != nil {
}); err != nil { return fmt.Errorf("push: %v", err)
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, "origin", "master"); err != nil {
return fmt.Errorf("Push: %v", err)
} }
return nil return nil
@@ -164,15 +161,12 @@ func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
message := "Delete page '" + title + "'" message := "Delete page '" + title + "'"
if err = git.AddChanges(localPath, true); err != nil { if err = git.RepoAdd(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("AddChanges: %v", err) return fmt.Errorf("add all changes: %v", err)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ } else if err = git.RepoCommit(localPath, doer.NewGitSig(), message); err != nil {
Committer: doer.NewGitSig(), return fmt.Errorf("commit changes: %v", err)
Message: message, } else if err = git.RepoPush(localPath, "origin", "master"); err != nil {
}); err != nil { return fmt.Errorf("push: %v", err)
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, "origin", "master"); err != nil {
return fmt.Errorf("Push: %v", err)
} }
return nil return nil

196
internal/gitutil/diff.go Normal file
View File

@@ -0,0 +1,196 @@
// Copyright 2014 The Gogs 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 gitutil
import (
"bytes"
"fmt"
"html"
"html/template"
"io"
"sync"
"github.com/sergi/go-diff/diffmatchpatch"
"golang.org/x/net/html/charset"
"golang.org/x/text/transform"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/template/highlight"
"gogs.io/gogs/internal/tool"
)
// DiffSection is a wrapper to git.DiffSection with helper methods.
type DiffSection struct {
*git.DiffSection
initOnce sync.Once
dmp *diffmatchpatch.DiffMatchPatch
}
// ComputedInlineDiffFor computes inline diff for the given line.
func (s *DiffSection) ComputedInlineDiffFor(line *git.DiffLine) template.HTML {
fallback := template.HTML(html.EscapeString(line.Content))
if conf.Git.DisableDiffHighlight {
return fallback
}
// Find equivalent diff line, ignore when not found.
var diff1, diff2 string
switch line.Type {
case git.DiffLineAdd:
compareLine := s.Line(git.DiffLineDelete, line.RightLine)
if compareLine == nil {
return fallback
}
diff1 = compareLine.Content
diff2 = line.Content
case git.DiffLineDelete:
compareLine := s.Line(git.DiffLineAdd, line.LeftLine)
if compareLine == nil {
return fallback
}
diff1 = line.Content
diff2 = compareLine.Content
default:
return fallback
}
s.initOnce.Do(func() {
s.dmp = diffmatchpatch.New()
s.dmp.DiffEditCost = 100
})
diffs := s.dmp.DiffMain(diff1[1:], diff2[1:], true)
diffs = s.dmp.DiffCleanupEfficiency(diffs)
return diffsToHTML(diffs, line.Type)
}
func diffsToHTML(diffs []diffmatchpatch.Diff, lineType git.DiffLineType) template.HTML {
buf := bytes.NewBuffer(nil)
// Reproduce signs which are cutted for inline diff before.
switch lineType {
case git.DiffLineAdd:
buf.WriteByte('+')
case git.DiffLineDelete:
buf.WriteByte('-')
}
buf.WriteByte(' ')
const (
addedCodePrefix = `<span class="added-code">`
removedCodePrefix = `<span class="removed-code">`
codeTagSuffix = `</span>`
)
for i := range diffs {
switch {
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == git.DiffLineAdd:
buf.WriteString(addedCodePrefix)
buf.WriteString(html.EscapeString(diffs[i].Text))
buf.WriteString(codeTagSuffix)
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == git.DiffLineDelete:
buf.WriteString(removedCodePrefix)
buf.WriteString(html.EscapeString(diffs[i].Text))
buf.WriteString(codeTagSuffix)
case diffs[i].Type == diffmatchpatch.DiffEqual:
buf.WriteString(html.EscapeString(diffs[i].Text))
}
}
return template.HTML(buf.Bytes())
}
// DiffFile is a wrapper to git.DiffFile with helper methods.
type DiffFile struct {
*git.DiffFile
Sections []*DiffSection
}
// HighlightClass returns the detected highlight class for the file.
func (diffFile *DiffFile) HighlightClass() string {
return highlight.FileNameToHighlightClass(diffFile.Name)
}
// Diff is a wrapper to git.Diff with helper methods.
type Diff struct {
*git.Diff
Files []*DiffFile
}
// NewDiff returns a new wrapper of given git.Diff.
func NewDiff(oldDiff *git.Diff) *Diff {
newDiff := &Diff{
Diff: oldDiff,
Files: make([]*DiffFile, oldDiff.NumFiles()),
}
// FIXME: detect encoding while parsing.
var buf bytes.Buffer
for i := range oldDiff.Files {
buf.Reset()
newDiff.Files[i] = &DiffFile{
DiffFile: oldDiff.Files[i],
Sections: make([]*DiffSection, oldDiff.Files[i].NumSections()),
}
for j := range oldDiff.Files[i].Sections {
newDiff.Files[i].Sections[j] = &DiffSection{
DiffSection: oldDiff.Files[i].Sections[j],
}
for k := range newDiff.Files[i].Sections[j].Lines {
buf.WriteString(newDiff.Files[i].Sections[j].Lines[k].Content)
buf.WriteString("\n")
}
}
charsetLabel, err := tool.DetectEncoding(buf.Bytes())
if charsetLabel != "UTF-8" && err == nil {
encoding, _ := charset.Lookup(charsetLabel)
if encoding != nil {
d := encoding.NewDecoder()
for j := range newDiff.Files[i].Sections {
for k := range newDiff.Files[i].Sections[j].Lines {
if c, _, err := transform.String(d, newDiff.Files[i].Sections[j].Lines[k].Content); err == nil {
newDiff.Files[i].Sections[j].Lines[k].Content = c
}
}
}
}
}
}
return newDiff
}
// ParseDiff parses the diff from given io.Reader.
func ParseDiff(r io.Reader, maxFiles, maxFileLines, maxLineChars int) (*Diff, error) {
done := make(chan git.SteamParseDiffResult)
go git.StreamParseDiff(r, done, maxFiles, maxFileLines, maxLineChars)
result := <-done
if result.Err != nil {
return nil, fmt.Errorf("stream parse diff: %v", result.Err)
}
return NewDiff(result.Diff), nil
}
// RepoDiff parses the diff on given revisions of given repository.
func RepoDiff(repo *git.Repository, rev string, maxFiles, maxFileLines, maxLineChars int, opts ...git.DiffOptions) (*Diff, error) {
diff, err := repo.Diff(rev, maxFiles, maxFileLines, maxLineChars, opts...)
if err != nil {
return nil, fmt.Errorf("get diff: %v", err)
}
return NewDiff(diff), nil
}

View File

@@ -0,0 +1,49 @@
// Copyright 2016 The Gogs 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 gitutil
import (
"html/template"
"testing"
dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
"github.com/gogs/git-module"
)
func Test_diffsToHTML(t *testing.T) {
tests := []struct {
diffs []dmp.Diff
lineType git.DiffLineType
expHTML template.HTML
}{
{
diffs: []dmp.Diff{
{Type: dmp.DiffEqual, Text: "foo "},
{Type: dmp.DiffInsert, Text: "bar"},
{Type: dmp.DiffDelete, Text: " baz"},
{Type: dmp.DiffEqual, Text: " biz"},
},
lineType: git.DiffLineAdd,
expHTML: template.HTML(`+ foo <span class="added-code">bar</span> biz`),
},
{
diffs: []dmp.Diff{
{Type: dmp.DiffEqual, Text: "foo "},
{Type: dmp.DiffDelete, Text: "bar"},
{Type: dmp.DiffInsert, Text: " baz"},
{Type: dmp.DiffEqual, Text: " biz"},
},
lineType: git.DiffLineDelete,
expHTML: template.HTML(`- foo <span class="removed-code">bar</span> biz`),
},
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
assert.Equal(t, test.expHTML, diffsToHTML(test.diffs, test.lineType))
})
}
}

19
internal/gitutil/error.go Normal file
View File

@@ -0,0 +1,19 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"github.com/gogs/git-module"
)
// IsErrRevisionNotExist returns true if the error is git.ErrRevisionNotExist.
func IsErrRevisionNotExist(err error) bool {
return err == git.ErrRevisionNotExist
}
// IsErrNoMergeBase returns true if the error is git.ErrNoMergeBase.
func IsErrNoMergeBase(err error) bool {
return err == git.ErrNoMergeBase
}

View File

@@ -0,0 +1,23 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"os"
"testing"
"github.com/gogs/git-module"
"github.com/stretchr/testify/assert"
)
func TestIsErrRevisionNotExist(t *testing.T) {
assert.True(t, IsErrRevisionNotExist(git.ErrRevisionNotExist))
assert.False(t, IsErrRevisionNotExist(os.ErrNotExist))
}
func TestIsErrNoMergeBase(t *testing.T) {
assert.True(t, IsErrNoMergeBase(git.ErrNoMergeBase))
assert.False(t, IsErrNoMergeBase(os.ErrNotExist))
}

23
internal/gitutil/mock.go Normal file
View File

@@ -0,0 +1,23 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"github.com/gogs/git-module"
)
type MockModuleStore struct {
RepoAddRemote func(repoPath, name, url string, opts ...git.AddRemoteOptions) error
RepoDiffNameOnly func(repoPath, base, head string, opts ...git.DiffNameOnlyOptions) ([]string, error)
RepoLog func(repoPath, rev string, opts ...git.LogOptions) ([]*git.Commit, error)
RepoMergeBase func(repoPath, base, head string, opts ...git.MergeBaseOptions) (string, error)
RepoRemoveRemote func(repoPath, name string, opts ...git.RemoveRemoteOptions) error
RepoTags func(repoPath string, opts ...git.TagsOptions) ([]string, error)
}
// MockModule holds mock implementation of each method for Modulers interface.
// When the field is non-nil, it is considered mocked. Otherwise, the real
// implementation will be executed.
var MockModule MockModuleStore

View File

@@ -0,0 +1,90 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"github.com/gogs/git-module"
)
// Moduler is the interface for Git operations.
//
// NOTE: All methods are sorted in alphabetically.
type Moduler interface {
// AddRemote adds a new remote to the repository in given path.
RepoAddRemote(repoPath, name, url string, opts ...git.AddRemoteOptions) error
// RepoDiffNameOnly returns a list of changed files between base and head revisions
// of the repository in given path.
RepoDiffNameOnly(repoPath, base, head string, opts ...git.DiffNameOnlyOptions) ([]string, error)
// RepoLog returns a list of commits in the state of given revision of the repository
// in given path. The returned list is in reverse chronological order.
RepoLog(repoPath, rev string, opts ...git.LogOptions) ([]*git.Commit, error)
// RepoMergeBase returns merge base between base and head revisions of the repository
// in given path.
RepoMergeBase(repoPath, base, head string, opts ...git.MergeBaseOptions) (string, error)
// RepoRemoveRemote removes a remote from the repository in given path.
RepoRemoveRemote(repoPath, name string, opts ...git.RemoveRemoteOptions) error
// RepoTags returns a list of tags of the repository in given path.
RepoTags(repoPath string, opts ...git.TagsOptions) ([]string, error)
Utiler
}
// Utiler is the interface for utility helpers implemented in this package.
//
// NOTE: All methods are sorted in alphabetically.
type Utiler interface {
// GetPullRequestMeta gathers pull request metadata based on given head and base information.
PullRequestMeta(headPath, basePath, headBranch, baseBranch string) (*PullRequestMeta, error)
// ListTagsAfter returns a list of tags "after" (exlusive) given tag.
ListTagsAfter(repoPath, after string, limit int) (*TagsPage, error)
}
// moduler is holds real implementation.
type moduler struct{}
func (moduler) RepoAddRemote(repoPath, name, url string, opts ...git.AddRemoteOptions) error {
if MockModule.RepoAddRemote != nil {
return MockModule.RepoAddRemote(repoPath, name, url, opts...)
}
return git.RepoAddRemote(repoPath, name, url, opts...)
}
func (moduler) RepoDiffNameOnly(repoPath, base, head string, opts ...git.DiffNameOnlyOptions) ([]string, error) {
if MockModule.RepoDiffNameOnly != nil {
return MockModule.RepoDiffNameOnly(repoPath, base, head, opts...)
}
return git.RepoDiffNameOnly(repoPath, base, head, opts...)
}
func (moduler) RepoLog(repoPath, rev string, opts ...git.LogOptions) ([]*git.Commit, error) {
if MockModule.RepoLog != nil {
return MockModule.RepoLog(repoPath, rev, opts...)
}
return git.RepoLog(repoPath, rev, opts...)
}
func (moduler) RepoMergeBase(repoPath, base, head string, opts ...git.MergeBaseOptions) (string, error) {
if MockModule.RepoMergeBase != nil {
return MockModule.RepoMergeBase(repoPath, base, head, opts...)
}
return git.RepoMergeBase(repoPath, base, head, opts...)
}
func (moduler) RepoRemoveRemote(repoPath, name string, opts ...git.RemoveRemoteOptions) error {
if MockModule.RepoRemoveRemote != nil {
return MockModule.RepoRemoveRemote(repoPath, name, opts...)
}
return git.RepoRemoveRemote(repoPath, name, opts...)
}
func (moduler) RepoTags(repoPath string, opts ...git.TagsOptions) ([]string, error) {
if MockModule.RepoTags != nil {
return MockModule.RepoTags(repoPath, opts...)
}
return git.RepoTags(repoPath, opts...)
}
// Module is a mockable interface for Git operations.
var Module Moduler = moduler{}

View File

@@ -0,0 +1,69 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"fmt"
"strconv"
"time"
"github.com/gogs/git-module"
"github.com/pkg/errors"
log "unknwon.dev/clog/v2"
)
// PullRequestMeta contains metadata for a pull request.
type PullRequestMeta struct {
// The merge base of the pull request.
MergeBase string
// The commits that are requested to be merged.
Commits []*git.Commit
// The number of files changed.
NumFiles int
}
func (moduler) PullRequestMeta(headPath, basePath, headBranch, baseBranch string) (*PullRequestMeta, error) {
tmpRemoteBranch := baseBranch
// We need to create a temporary remote when the pull request is sent from a forked repository.
if headPath != basePath {
tmpRemote := strconv.FormatInt(time.Now().UnixNano(), 10)
err := Module.RepoAddRemote(headPath, tmpRemote, basePath, git.AddRemoteOptions{Fetch: true})
if err != nil {
return nil, fmt.Errorf("add remote: %v", err)
}
defer func() {
err := Module.RepoRemoveRemote(headPath, tmpRemote)
if err != nil {
log.Error("Failed to remove remote %q [path: %s]: %v", tmpRemote, headPath, err)
return
}
}()
tmpRemoteBranch = "remotes/" + tmpRemote + "/" + baseBranch
}
mergeBase, err := Module.RepoMergeBase(headPath, tmpRemoteBranch, headBranch)
if err != nil {
return nil, errors.Wrap(err, "get merge base")
}
commits, err := Module.RepoLog(headPath, mergeBase+"..."+headBranch)
if err != nil {
return nil, errors.Wrap(err, "get commits")
}
// Count number of changed files
names, err := Module.RepoDiffNameOnly(headPath, tmpRemoteBranch, headBranch, git.DiffNameOnlyOptions{NeedsMergeBase: true})
if err != nil {
return nil, errors.Wrap(err, "get changed files")
}
return &PullRequestMeta{
MergeBase: mergeBase,
Commits: commits,
NumFiles: len(names),
}, nil
}

View File

@@ -0,0 +1,108 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"fmt"
"testing"
"github.com/gogs/git-module"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestModuler_PullRequestMeta(t *testing.T) {
headPath := "/head/path"
basePath := "/base/path"
headBranch := "head_branch"
baseBranch := "base_branch"
mergeBase := "MERGE-BASE"
changedFiles := []string{"a.go", "b.txt"}
commits := []*git.Commit{
{ID: git.MustIDFromString("adfd6da3c0a3fb038393144becbf37f14f780087")},
}
MockModule.RepoAddRemote = func(repoPath, name, url string, opts ...git.AddRemoteOptions) error {
if repoPath != headPath {
return fmt.Errorf("repoPath: want %q but got %q", headPath, repoPath)
} else if name == "" {
return errors.New("empty name")
} else if url != basePath {
return fmt.Errorf("url: want %q but got %q", basePath, url)
}
if len(opts) == 0 {
return errors.New("no options")
} else if !opts[0].Fetch {
return fmt.Errorf("opts.Fetch: want %v but got %v", true, opts[0].Fetch)
}
return nil
}
MockModule.RepoMergeBase = func(repoPath, base, head string, opts ...git.MergeBaseOptions) (string, error) {
if repoPath != headPath {
return "", fmt.Errorf("repoPath: want %q but got %q", headPath, repoPath)
} else if base == "" {
return "", errors.New("empty base")
} else if head != headBranch {
return "", fmt.Errorf("head: want %q but got %q", headBranch, head)
}
return mergeBase, nil
}
MockModule.RepoLog = func(repoPath, rev string, opts ...git.LogOptions) ([]*git.Commit, error) {
if repoPath != headPath {
return nil, fmt.Errorf("repoPath: want %q but got %q", headPath, repoPath)
}
expRev := mergeBase + "..." + headBranch
if rev != expRev {
return nil, fmt.Errorf("rev: want %q but got %q", expRev, rev)
}
return commits, nil
}
MockModule.RepoDiffNameOnly = func(repoPath, base, head string, opts ...git.DiffNameOnlyOptions) ([]string, error) {
if repoPath != headPath {
return nil, fmt.Errorf("repoPath: want %q but got %q", headPath, repoPath)
} else if base == "" {
return nil, errors.New("empty base")
} else if head != headBranch {
return nil, fmt.Errorf("head: want %q but got %q", headBranch, head)
}
if len(opts) == 0 {
return nil, errors.New("no options")
} else if !opts[0].NeedsMergeBase {
return nil, fmt.Errorf("opts.NeedsMergeBase: want %v but got %v", true, opts[0].NeedsMergeBase)
}
return changedFiles, nil
}
MockModule.RepoRemoveRemote = func(repoPath, name string, opts ...git.RemoveRemoteOptions) error {
if repoPath != headPath {
return fmt.Errorf("repoPath: want %q but got %q", headPath, repoPath)
} else if name == "" {
return errors.New("empty name")
}
return nil
}
defer func() {
MockModule = MockModuleStore{}
}()
meta, err := Module.PullRequestMeta(headPath, basePath, headBranch, baseBranch)
if err != nil {
t.Fatal(err)
}
expMeta := &PullRequestMeta{
MergeBase: mergeBase,
Commits: commits,
NumFiles: 2,
}
assert.Equal(t, expMeta, meta)
}

View File

@@ -0,0 +1,48 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"fmt"
"net/url"
"strings"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/lazyregexp"
)
var scpSyntax = lazyregexp.New(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`)
// InferSubmoduleURL returns the inferred external URL of the submodule at best effort.
func InferSubmoduleURL(mod *git.Submodule) string {
raw := strings.TrimSuffix(mod.URL, "/")
raw = strings.TrimSuffix(raw, ".git")
parsed, err := url.Parse(raw)
if err != nil {
// Try parse as SCP syntax again
match := scpSyntax.FindAllStringSubmatch(raw, -1)
if len(match) == 0 {
return mod.URL
}
parsed = &url.URL{
Scheme: "http",
Host: match[0][2],
Path: match[0][3],
}
}
switch parsed.Scheme {
case "http", "https":
raw = parsed.String()
case "ssh":
raw = fmt.Sprintf("http://%s%s", parsed.Host, parsed.Path)
default:
return raw
}
return fmt.Sprintf("%s/commit/%s", raw, mod.Commit)
}

View File

@@ -0,0 +1,58 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"testing"
"github.com/gogs/git-module"
"github.com/stretchr/testify/assert"
)
func TestInferSubmoduleURL(t *testing.T) {
tests := []struct {
name string
submodule *git.Submodule
expURL string
}{
{
name: "HTTPS URL",
submodule: &git.Submodule{
URL: "https://github.com/gogs/docs-api.git",
Commit: "6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
expURL: "https://github.com/gogs/docs-api/commit/6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
{
name: "SSH URL with port",
submodule: &git.Submodule{
URL: "ssh://user@github.com:22/gogs/docs-api.git",
Commit: "6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
expURL: "http://github.com:22/gogs/docs-api/commit/6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
{
name: "SSH URL in SCP syntax",
submodule: &git.Submodule{
URL: "git@github.com:gogs/docs-api.git",
Commit: "6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
expURL: "http://github.com/gogs/docs-api/commit/6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
{
name: "bad URL",
submodule: &git.Submodule{
URL: "ftp://example.com",
Commit: "6b08f76a5313fa3d26859515b30aa17a5faa2807",
},
expURL: "ftp://example.com",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expURL, InferSubmoduleURL(test.submodule))
})
}
}

95
internal/gitutil/tag.go Normal file
View File

@@ -0,0 +1,95 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"github.com/pkg/errors"
)
// TagsPage contains a list of tags and pagination information.
type TagsPage struct {
// List of tags in the current page.
Tags []string
// Whether the results include the latest tag.
HasLatest bool
// When results do not include the latest tag, an indicator of 'after' to go back.
PreviousAfter string
// Whether there are more tags in the next page.
HasNext bool
}
func (moduler) ListTagsAfter(repoPath, after string, limit int) (*TagsPage, error) {
all, err := Module.RepoTags(repoPath)
if err != nil {
return nil, errors.Wrap(err, "get tags")
}
total := len(all)
if limit < 0 {
limit = 0
}
// Returns everything when no filter and no limit
if after == "" && limit == 0 {
return &TagsPage{
Tags: all,
HasLatest: true,
}, nil
}
// No filter but has a limit, returns first X tags
if after == "" && limit > 0 {
endIdx := limit
if limit > total {
endIdx = total
}
return &TagsPage{
Tags: all[:endIdx],
HasLatest: true,
HasNext: limit < total,
}, nil
}
// Loop over all tags see if we can find the filter
previousAfter := ""
found := false
tags := make([]string, 0, len(all))
for i := range all {
if all[i] != after {
continue
}
found = true
if limit > 0 && i-limit >= 0 {
previousAfter = all[i-limit]
}
// In case filter is the oldest one
if i+1 < total {
tags = all[i+1:]
}
break
}
if !found {
tags = all
}
// If all tags after match is equal to the limit, it reaches the oldest tag as well.
if limit == 0 || len(tags) <= limit {
return &TagsPage{
Tags: tags,
HasLatest: !found,
PreviousAfter: previousAfter,
}, nil
}
return &TagsPage{
Tags: tags[:limit],
HasLatest: !found,
PreviousAfter: previousAfter,
HasNext: true,
}, nil
}

View File

@@ -0,0 +1,109 @@
// Copyright 2020 The Gogs 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 gitutil
import (
"testing"
"github.com/gogs/git-module"
"github.com/stretchr/testify/assert"
)
func TestModuler_ListTagsAfter(t *testing.T) {
MockModule.RepoTags = func(string, ...git.TagsOptions) ([]string, error) {
return []string{
"v2.3.0", "v2.2.1", "v2.1.0",
"v1.3.0", "v1.2.0", "v1.1.0",
"v0.8.0", "v0.5.0", "v0.1.0",
}, nil
}
defer func() {
MockModule = MockModuleStore{}
}()
tests := []struct {
name string
after string
expTagsPage *TagsPage
}{
{
name: "first page",
expTagsPage: &TagsPage{
Tags: []string{
"v2.3.0", "v2.2.1", "v2.1.0",
},
HasLatest: true,
HasNext: true,
},
},
{
name: "second page",
after: "v2.1.0",
expTagsPage: &TagsPage{
Tags: []string{
"v1.3.0", "v1.2.0", "v1.1.0",
},
HasLatest: false,
HasNext: true,
},
},
{
name: "last page",
after: "v1.1.0",
expTagsPage: &TagsPage{
Tags: []string{
"v0.8.0", "v0.5.0", "v0.1.0",
},
HasLatest: false,
PreviousAfter: "v2.1.0",
HasNext: false,
},
},
{
name: "arbitrary after",
after: "v1.2.0",
expTagsPage: &TagsPage{
Tags: []string{
"v1.1.0", "v0.8.0", "v0.5.0",
},
HasLatest: false,
PreviousAfter: "v2.2.1",
HasNext: true,
},
},
{
name: "after the oldest one",
after: "v0.1.0",
expTagsPage: &TagsPage{
Tags: []string{},
HasLatest: false,
PreviousAfter: "v1.1.0",
HasNext: false,
},
},
{
name: "after does not exist",
after: "v2.2.9",
expTagsPage: &TagsPage{
Tags: []string{
"v2.3.0", "v2.2.1", "v2.1.0",
},
HasLatest: true,
HasNext: true,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tagsPage, err := Module.ListTagsAfter("", test.after, 3)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expTagsPage, tagsPage)
})
}
}

View File

@@ -43,7 +43,7 @@ func ToCommit(c *git.Commit) *api.PayloadCommit {
} }
return &api.PayloadCommit{ return &api.PayloadCommit{
ID: c.ID.String(), ID: c.ID.String(),
Message: c.Message(), Message: c.Message,
URL: "Not implemented", URL: "Not implemented",
Author: &api.PayloadUser{ Author: &api.PayloadUser{
Name: c.Author.Name, Name: c.Author.Name,

View File

@@ -16,6 +16,7 @@ import (
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/gitutil"
) )
func GetSingleCommit(c *context.APIContext) { func GetSingleCommit(c *context.APIContext) {
@@ -25,14 +26,14 @@ func GetSingleCommit(c *context.APIContext) {
return return
} }
gitRepo, err := git.OpenRepository(c.Repo.Repository.RepoPath()) gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
if err != nil { if err != nil {
c.ServerError("OpenRepository", err) c.ServerError("open repository", err)
return return
} }
commit, err := gitRepo.GetCommit(c.Params(":sha")) commit, err := gitRepo.CatFileCommit(c.Params(":sha"))
if err != nil { if err != nil {
c.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err) c.NotFoundOrServerError("get commit", gitutil.IsErrRevisionNotExist, err)
return return
} }
@@ -59,8 +60,8 @@ func GetSingleCommit(c *context.APIContext) {
} }
// Retrieve parent(s) of the commit // Retrieve parent(s) of the commit
apiParents := make([]*api.CommitMeta, commit.ParentCount()) apiParents := make([]*api.CommitMeta, commit.ParentsCount())
for i := 0; i < commit.ParentCount(); i++ { for i := 0; i < commit.ParentsCount(); i++ {
sha, _ := commit.ParentID(i) sha, _ := commit.ParentID(i)
apiParents[i] = &api.CommitMeta{ apiParents[i] = &api.CommitMeta{
URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(), URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(),
@@ -99,24 +100,24 @@ func GetSingleCommit(c *context.APIContext) {
} }
func GetReferenceSHA(c *context.APIContext) { func GetReferenceSHA(c *context.APIContext) {
gitRepo, err := git.OpenRepository(c.Repo.Repository.RepoPath()) gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
if err != nil { if err != nil {
c.ServerError("OpenRepository", err) c.ServerError("open repository", err)
return return
} }
ref := c.Params("*") ref := c.Params("*")
refType := 0 // 0-undetermined, 1-branch, 2-tag refType := 0 // 0-unknown, 1-branch, 2-tag
if strings.HasPrefix(ref, git.BRANCH_PREFIX) { if strings.HasPrefix(ref, git.RefsHeads) {
ref = strings.TrimPrefix(ref, git.BRANCH_PREFIX) ref = strings.TrimPrefix(ref, git.RefsHeads)
refType = 1 refType = 1
} else if strings.HasPrefix(ref, git.TAG_PREFIX) { } else if strings.HasPrefix(ref, git.RefsTags) {
ref = strings.TrimPrefix(ref, git.TAG_PREFIX) ref = strings.TrimPrefix(ref, git.RefsTags)
refType = 2 refType = 2
} else { } else {
if gitRepo.IsBranchExist(ref) { if gitRepo.HasBranch(ref) {
refType = 1 refType = 1
} else if gitRepo.IsTagExist(ref) { } else if gitRepo.HasTag(ref) {
refType = 2 refType = 2
} else { } else {
c.NotFound() c.NotFound()
@@ -126,12 +127,12 @@ func GetReferenceSHA(c *context.APIContext) {
var sha string var sha string
if refType == 1 { if refType == 1 {
sha, err = gitRepo.GetBranchCommitID(ref) sha, err = gitRepo.BranchCommitID(ref)
} else if refType == 2 { } else if refType == 2 {
sha, err = gitRepo.GetTagCommitID(ref) sha, err = gitRepo.TagCommitID(ref)
} }
if err != nil { if err != nil {
c.NotFoundOrServerError("get reference commit ID", git.IsErrNotExist, err) c.NotFoundOrServerError("get reference commit ID", gitutil.IsErrRevisionNotExist, err)
return return
} }
c.PlainText(http.StatusOK, []byte(sha)) c.PlainText(http.StatusOK, []byte(sha))

View File

@@ -7,11 +7,9 @@ package repo
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io/ioutil"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/gitutil"
) )
type repoContent struct { type repoContent struct {
@@ -38,9 +36,9 @@ type Links struct {
} }
func GetContents(c *context.APIContext) { func GetContents(c *context.APIContext) {
treeEntry, err := c.Repo.Commit.GetTreeEntryByPath(c.Repo.TreePath) treeEntry, err := c.Repo.Commit.TreeEntry(c.Repo.TreePath)
if err != nil { if err != nil {
c.NotFoundOrServerError("GetTreeEntryByPath", git.IsErrNotExist, err) c.NotFoundOrServerError("get tree entry", gitutil.IsErrRevisionNotExist, err)
return return
} }
username := c.Params(":username") username := c.Params(":username")
@@ -56,8 +54,8 @@ func GetContents(c *context.APIContext) {
// :baseurl/repos/:username/:project/tree/:sha // :baseurl/repos/:username/:project/tree/:sha
templateHTMLLLink := "%s/repos/%s/%s/tree/%s" templateHTMLLLink := "%s/repos/%s/%s/tree/%s"
gitURL := fmt.Sprintf(templateGitURLLink, c.BaseURL, username, reponame, treeEntry.ID.String()) gitURL := fmt.Sprintf(templateGitURLLink, c.BaseURL, username, reponame, treeEntry.ID().String())
htmlURL := fmt.Sprintf(templateHTMLLLink, c.BaseURL, username, reponame, treeEntry.ID.String()) htmlURL := fmt.Sprintf(templateHTMLLLink, c.BaseURL, username, reponame, treeEntry.ID().String())
selfURL := fmt.Sprintf(templateSelfLink, c.BaseURL, username, reponame, c.Repo.TreePath) selfURL := fmt.Sprintf(templateSelfLink, c.BaseURL, username, reponame, c.Repo.TreePath)
// TODO(unknwon): Make a treeEntryToRepoContent helper. // TODO(unknwon): Make a treeEntryToRepoContent helper.
@@ -65,7 +63,7 @@ func GetContents(c *context.APIContext) {
Size: treeEntry.Size(), Size: treeEntry.Size(),
Name: treeEntry.Name(), Name: treeEntry.Name(),
Path: c.Repo.TreePath, Path: c.Repo.TreePath,
Sha: treeEntry.ID.String(), Sha: treeEntry.ID().String(),
URL: selfURL, URL: selfURL,
GitURL: gitURL, GitURL: gitURL,
HTMLURL: htmlURL, HTMLURL: htmlURL,
@@ -82,65 +80,57 @@ func GetContents(c *context.APIContext) {
// 2. SubModule // 2. SubModule
// 3. SymLink // 3. SymLink
// 4. Blob (file) // 4. Blob (file)
if treeEntry.IsSubModule() { if treeEntry.IsCommit() {
// TODO(unknwon): submoduleURL is not set as current git-module doesn't handle it properly // TODO(unknwon): submoduleURL is not set as current git-module doesn't handle it properly
contents.Type = "submodule" contents.Type = "submodule"
c.JSONSuccess(contents) c.JSONSuccess(contents)
return return
} else if treeEntry.IsLink() { } else if treeEntry.IsSymlink() {
contents.Type = "symlink" contents.Type = "symlink"
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) blob, err := c.Repo.Commit.Blob(c.Repo.TreePath)
if err != nil { if err != nil {
c.ServerError("GetBlobByPath", err) c.ServerError("GetBlobByPath", err)
return return
} }
b, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
c.ServerError("Data", err) c.ServerError("Data", err)
return return
} }
buf, err := ioutil.ReadAll(b) contents.Target = string(p)
if err != nil {
c.ServerError("ReadAll", err)
return
}
contents.Target = string(buf)
c.JSONSuccess(contents) c.JSONSuccess(contents)
return return
} else if treeEntry.Type == "blob" { } else if treeEntry.IsBlob() {
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) blob, err := c.Repo.Commit.Blob(c.Repo.TreePath)
if err != nil { if err != nil {
c.ServerError("GetBlobByPath", err) c.ServerError("GetBlobByPath", err)
return return
} }
b, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
c.ServerError("Data", err) c.ServerError("Data", err)
return return
} }
buf, err := ioutil.ReadAll(b) contents.Content = base64.StdEncoding.EncodeToString(p)
if err != nil {
c.ServerError("ReadAll", err)
return
}
contents.Content = base64.StdEncoding.EncodeToString(buf)
contents.Type = "file" contents.Type = "file"
c.JSONSuccess(contents) c.JSONSuccess(contents)
return return
} }
// TODO: treeEntry.IsExec()
// treeEntry is a directory // treeEntry is a directory
dirTree, err := c.Repo.GitRepo.GetTree(treeEntry.ID.String()) dirTree, err := c.Repo.GitRepo.LsTree(treeEntry.ID().String())
if err != nil { if err != nil {
c.NotFoundOrServerError("GetTree", git.IsErrNotExist, err) c.NotFoundOrServerError("get tree", gitutil.IsErrRevisionNotExist, err)
return return
} }
entries, err := dirTree.ListEntries() entries, err := dirTree.Entries()
if err != nil { if err != nil {
c.NotFoundOrServerError("ListEntries", git.IsErrNotExist, err) c.NotFoundOrServerError("list entries", gitutil.IsErrRevisionNotExist, err)
return return
} }
@@ -151,33 +141,28 @@ func GetContents(c *context.APIContext) {
var results = make([]*repoContent, 0, len(entries)) var results = make([]*repoContent, 0, len(entries))
for _, entry := range entries { for _, entry := range entries {
gitURL := fmt.Sprintf(templateGitURLLink, c.BaseURL, username, reponame, entry.ID.String()) gitURL := fmt.Sprintf(templateGitURLLink, c.BaseURL, username, reponame, entry.ID().String())
htmlURL := fmt.Sprintf(templateHTMLLLink, c.BaseURL, username, reponame, entry.ID.String()) htmlURL := fmt.Sprintf(templateHTMLLLink, c.BaseURL, username, reponame, entry.ID().String())
selfURL := fmt.Sprintf(templateSelfLink, c.BaseURL, username, reponame, c.Repo.TreePath) selfURL := fmt.Sprintf(templateSelfLink, c.BaseURL, username, reponame, c.Repo.TreePath)
var contentType string var contentType string
if entry.IsDir() { if entry.IsTree() {
contentType = "dir" contentType = "dir"
} else if entry.IsSubModule() { } else if entry.IsCommit() {
// TODO(unknwon): submoduleURL is not set as current git-module doesn't handle it properly // TODO(unknwon): submoduleURL is not set as current git-module doesn't handle it properly
contentType = "submodule" contentType = "submodule"
} else if entry.IsLink() { } else if entry.IsSymlink() {
contentType = "symlink" contentType = "symlink"
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) blob, err := c.Repo.Commit.Blob(c.Repo.TreePath)
if err != nil { if err != nil {
c.ServerError("GetBlobByPath", err) c.ServerError("GetBlobByPath", err)
return return
} }
b, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
c.ServerError("Data", err) c.ServerError("Data", err)
return return
} }
buf, err := ioutil.ReadAll(b) contents.Target = string(p)
if err != nil {
c.ServerError("ReadAll", err)
return
}
contents.Target = string(buf)
} else { } else {
contentType = "file" contentType = "file"
} }
@@ -187,7 +172,7 @@ func GetContents(c *context.APIContext) {
Size: entry.Size(), Size: entry.Size(),
Name: entry.Name(), Name: entry.Name(),
Path: c.Repo.TreePath, Path: c.Repo.TreePath,
Sha: entry.ID.String(), Sha: entry.ID().String(),
URL: selfURL, URL: selfURL,
GitURL: gitURL, GitURL: gitURL,
HTMLURL: htmlURL, HTMLURL: htmlURL,

View File

@@ -9,6 +9,7 @@ import (
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/route/repo" "gogs.io/gogs/internal/route/repo"
) )
@@ -23,9 +24,9 @@ func GetRawFile(c *context.APIContext) {
return return
} }
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) blob, err := c.Repo.Commit.Blob(c.Repo.TreePath)
if err != nil { if err != nil {
c.NotFoundOrServerError("GetBlobByPath", git.IsErrNotExist, err) c.NotFoundOrServerError("get blob", gitutil.IsErrRevisionNotExist, err)
return return
} }
if err = repo.ServeBlob(c.Context, blob); err != nil { if err = repo.ServeBlob(c.Context, blob); err != nil {
@@ -35,9 +36,9 @@ func GetRawFile(c *context.APIContext) {
func GetArchive(c *context.APIContext) { func GetArchive(c *context.APIContext) {
repoPath := db.RepoPath(c.Params(":username"), c.Params(":reponame")) repoPath := db.RepoPath(c.Params(":username"), c.Params(":reponame"))
gitRepo, err := git.OpenRepository(repoPath) gitRepo, err := git.Open(repoPath)
if err != nil { if err != nil {
c.ServerError("OpenRepository", err) c.ServerError("open repository", err)
return return
} }
c.Repo.GitRepo = gitRepo c.Repo.GitRepo = gitRepo
@@ -46,16 +47,16 @@ func GetArchive(c *context.APIContext) {
} }
func GetEditorconfig(c *context.APIContext) { func GetEditorconfig(c *context.APIContext) {
ec, err := c.Repo.GetEditorconfig() ec, err := c.Repo.Editorconfig()
if err != nil { if err != nil {
c.NotFoundOrServerError("GetEditorconfig", git.IsErrNotExist, err) c.NotFoundOrServerError("get .editorconfig", gitutil.IsErrRevisionNotExist, err)
return return
} }
fileName := c.Params("filename") fileName := c.Params("filename")
def, err := ec.GetDefinitionForFilename(fileName) def, err := ec.GetDefinitionForFilename(fileName)
if err != nil { if err != nil {
c.ServerError("GetDefinitionForFilename", err) c.ServerError("get definition for filename", err)
return return
} }
if def == nil { if def == nil {

View File

@@ -1,3 +1,7 @@
// Copyright 2020 The Gogs 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 repo package repo
import ( import (
@@ -6,6 +10,7 @@ import (
"github.com/gogs/git-module" "github.com/gogs/git-module"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/gitutil"
) )
type repoGitTree struct { type repoGitTree struct {
@@ -24,14 +29,14 @@ type repoGitTreeEntry struct {
} }
func GetRepoGitTree(c *context.APIContext) { func GetRepoGitTree(c *context.APIContext) {
gitTree, err := c.Repo.GitRepo.GetTree(c.Params(":sha")) gitTree, err := c.Repo.GitRepo.LsTree(c.Params(":sha"))
if err != nil { if err != nil {
c.NotFoundOrServerError("GetRepoGitTree", git.IsErrNotExist, err) c.NotFoundOrServerError("get tree", gitutil.IsErrRevisionNotExist, err)
return return
} }
entries, err := gitTree.ListEntries() entries, err := gitTree.Entries()
if err != nil { if err != nil {
c.ServerError("GetRepoGitTree", err) c.ServerError("list entries", err)
return return
} }
@@ -48,7 +53,7 @@ func GetRepoGitTree(c *context.APIContext) {
children := make([]*repoGitTreeEntry, 0, len(entries)) children := make([]*repoGitTreeEntry, 0, len(entries))
for _, entry := range entries { for _, entry := range entries {
var mode string var mode string
switch entry.Type { switch entry.Type() {
case git.ObjectCommit: case git.ObjectCommit:
mode = "160000" mode = "160000"
case git.ObjectTree: case git.ObjectTree:
@@ -63,10 +68,10 @@ func GetRepoGitTree(c *context.APIContext) {
children = append(children, &repoGitTreeEntry{ children = append(children, &repoGitTreeEntry{
Path: entry.Name(), Path: entry.Name(),
Mode: mode, Mode: mode,
Type: string(entry.Type), Type: string(entry.Type()),
Size: entry.Size(), Size: entry.Size(),
Sha: entry.ID.String(), Sha: entry.ID().String(),
URL: fmt.Sprintf(templateURL+"/%s", entry.ID.String()), URL: fmt.Sprintf(templateURL+"/%s", entry.ID().String()),
}) })
} }
c.JSONSuccess(&repoGitTree{ c.JSONSuccess(&repoGitTree{

View File

@@ -41,9 +41,9 @@ func checkRunMode() {
if conf.IsProdMode() { if conf.IsProdMode() {
macaron.Env = macaron.PROD macaron.Env = macaron.PROD
macaron.ColorLog = false macaron.ColorLog = false
git.Debug = false git.SetOutput(nil)
} else { } else {
git.Debug = true git.SetOutput(os.Stdout)
} }
log.Info("Run mode: %s", strings.Title(macaron.Env)) log.Info("Run mode: %s", strings.Title(macaron.Env))
} }

View File

@@ -119,11 +119,11 @@ func DeleteBranchPost(c *context.Context) {
c.Redirect(redirectTo) c.Redirect(redirectTo)
}() }()
if !c.Repo.GitRepo.IsBranchExist(branchName) { if !c.Repo.GitRepo.HasBranch(branchName) {
return return
} }
if len(commitID) > 0 { if len(commitID) > 0 {
branchCommitID, err := c.Repo.GitRepo.GetBranchCommitID(branchName) branchCommitID, err := c.Repo.GitRepo.BranchCommitID(branchName)
if err != nil { if err != nil {
log.Error("Failed to get commit ID of branch %q: %v", branchName, err) log.Error("Failed to get commit ID of branch %q: %v", branchName, err)
return return

View File

@@ -5,7 +5,6 @@
package repo package repo
import ( import (
"container/list"
"path" "path"
"github.com/gogs/git-module" "github.com/gogs/git-module"
@@ -13,6 +12,7 @@ import (
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
) )
@@ -33,13 +33,9 @@ func RefCommits(c *context.Context) {
} }
} }
func RenderIssueLinks(oldCommits *list.List, repoLink string) *list.List { // TODO(unknwon)
newCommits := list.New() func RenderIssueLinks(oldCommits []*git.Commit, repoLink string) []*git.Commit {
for e := oldCommits.Front(); e != nil; e = e.Next() { return oldCommits
c := e.Value.(*git.Commit)
newCommits.PushBack(c)
}
return newCommits
} }
func renderCommits(c *context.Context, filename string) { func renderCommits(c *context.Context, filename string) {
@@ -53,30 +49,23 @@ func renderCommits(c *context.Context, filename string) {
} }
pageSize := c.QueryInt("pageSize") pageSize := c.QueryInt("pageSize")
if pageSize < 1 { if pageSize < 1 {
pageSize = git.DefaultCommitsPageSize pageSize = conf.UI.User.CommitsPagingNum
} }
// Both 'git log branchName' and 'git log commitID' work. commits, err := c.Repo.Commit.CommitsByPage(page, pageSize, git.CommitsByPageOptions{Path: filename})
var err error
var commits *list.List
if len(filename) == 0 {
commits, err = c.Repo.Commit.CommitsByRangeSize(page, pageSize)
} else {
commits, err = c.Repo.GitRepo.CommitsByFileAndRangeSize(c.Repo.BranchName, filename, page, pageSize)
}
if err != nil { if err != nil {
c.Handle(500, "CommitsByRangeSize/CommitsByFileAndRangeSize", err) c.ServerError("paging commits", err)
return return
} }
commits = RenderIssueLinks(commits, c.Repo.RepoLink) commits = RenderIssueLinks(commits, c.Repo.RepoLink)
commits = db.ValidateCommitsWithEmails(commits) c.Data["Commits"] = db.ValidateCommitsWithEmails(commits)
c.Data["Commits"] = commits
if page > 1 { if page > 1 {
c.Data["HasPrevious"] = true c.Data["HasPrevious"] = true
c.Data["PreviousPage"] = page - 1 c.Data["PreviousPage"] = page - 1
} }
if commits.Len() == pageSize { if len(commits) == pageSize {
c.Data["HasNext"] = true c.Data["HasNext"] = true
c.Data["NextPage"] = page + 1 c.Data["NextPage"] = page + 1
} }
@@ -102,12 +91,12 @@ func SearchCommits(c *context.Context) {
commits, err := c.Repo.Commit.SearchCommits(keyword) commits, err := c.Repo.Commit.SearchCommits(keyword)
if err != nil { if err != nil {
c.Handle(500, "SearchCommits", err) c.ServerError("SearchCommits", err)
return return
} }
commits = RenderIssueLinks(commits, c.Repo.RepoLink) commits = RenderIssueLinks(commits, c.Repo.RepoLink)
commits = db.ValidateCommitsWithEmails(commits) c.Data["Commits"] = db.ValidateCommitsWithEmails(commits)
c.Data["Commits"] = commits
c.Data["Keyword"] = keyword c.Data["Keyword"] = keyword
c.Data["Username"] = c.Repo.Owner.Name c.Data["Username"] = c.Repo.Owner.Name
@@ -128,22 +117,23 @@ func Diff(c *context.Context) {
repoName := c.Repo.Repository.Name repoName := c.Repo.Repository.Name
commitID := c.Params(":sha") commitID := c.Params(":sha")
commit, err := c.Repo.GitRepo.GetCommit(commitID) commit, err := c.Repo.GitRepo.CatFileCommit(commitID)
if err != nil { if err != nil {
c.NotFoundOrServerError("get commit by ID", git.IsErrNotExist, err) // TODO: Move checker to gitutil package
c.NotFoundOrServerError("get commit by ID", gitutil.IsErrRevisionNotExist, err)
return return
} }
diff, err := db.GetDiffCommit(db.RepoPath(userName, repoName), diff, err := gitutil.RepoDiff(c.Repo.GitRepo,
commitID, conf.Git.MaxGitDiffLines, commitID, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars,
conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles) )
if err != nil { if err != nil {
c.NotFoundOrServerError("get diff commit", git.IsErrNotExist, err) c.NotFoundOrServerError("get diff", gitutil.IsErrRevisionNotExist, err)
return return
} }
parents := make([]string, commit.ParentCount()) parents := make([]string, commit.ParentsCount())
for i := 0; i < commit.ParentCount(); i++ { for i := 0; i < commit.ParentsCount(); i++ {
sha, err := commit.ParentID(i) sha, err := commit.ParentID(i)
parents[i] = sha.String() parents[i] = sha.String()
if err != nil { if err != nil {
@@ -169,7 +159,7 @@ func Diff(c *context.Context) {
c.Data["Parents"] = parents c.Data["Parents"] = parents
c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 c.Data["DiffNotAvailable"] = diff.NumFiles() == 0
c.Data["SourcePath"] = conf.Server.Subpath + "/" + path.Join(userName, repoName, "src", commitID) c.Data["SourcePath"] = conf.Server.Subpath + "/" + path.Join(userName, repoName, "src", commitID)
if commit.ParentCount() > 0 { if commit.ParentsCount() > 0 {
c.Data["BeforeSourcePath"] = conf.Server.Subpath + "/" + path.Join(userName, repoName, "src", parents[0]) c.Data["BeforeSourcePath"] = conf.Server.Subpath + "/" + path.Join(userName, repoName, "src", parents[0])
} }
c.Data["RawPath"] = conf.Server.Subpath + "/" + path.Join(userName, repoName, "raw", commitID) c.Data["RawPath"] = conf.Server.Subpath + "/" + path.Join(userName, repoName, "raw", commitID)
@@ -177,13 +167,12 @@ func Diff(c *context.Context) {
} }
func RawDiff(c *context.Context) { func RawDiff(c *context.Context) {
if err := git.GetRawDiff( if err := c.Repo.GitRepo.RawDiff(
db.RepoPath(c.Repo.Owner.Name, c.Repo.Repository.Name),
c.Params(":sha"), c.Params(":sha"),
git.RawDiffType(c.Params(":ext")), git.RawDiffFormat(c.Params(":ext")),
c.Resp, c.Resp,
); err != nil { ); err != nil {
c.NotFoundOrServerError("GetRawDiff", git.IsErrNotExist, err) c.NotFoundOrServerError("get raw diff", gitutil.IsErrRevisionNotExist, err)
return return
} }
} }
@@ -195,31 +184,31 @@ func CompareDiff(c *context.Context) {
beforeCommitID := c.Params(":before") beforeCommitID := c.Params(":before")
afterCommitID := c.Params(":after") afterCommitID := c.Params(":after")
commit, err := c.Repo.GitRepo.GetCommit(afterCommitID) commit, err := c.Repo.GitRepo.CatFileCommit(afterCommitID)
if err != nil { if err != nil {
c.Handle(404, "GetCommit", err) c.Handle(404, "GetCommit", err)
return return
} }
diff, err := db.GetDiffRange(db.RepoPath(userName, repoName), beforeCommitID, diff, err := gitutil.RepoDiff(c.Repo.GitRepo,
afterCommitID, conf.Git.MaxGitDiffLines, afterCommitID, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars,
conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles) git.DiffOptions{Base: beforeCommitID},
)
if err != nil { if err != nil {
c.Handle(404, "GetDiffRange", err) c.ServerError("get diff", err)
return return
} }
commits, err := commit.CommitsBeforeUntil(beforeCommitID) commits, err := commit.CommitsAfter(beforeCommitID)
if err != nil { if err != nil {
c.Handle(500, "CommitsBeforeUntil", err) c.ServerError("get commits after", err)
return return
} }
commits = db.ValidateCommitsWithEmails(commits)
c.Data["IsSplitStyle"] = c.Query("style") == "split" c.Data["IsSplitStyle"] = c.Query("style") == "split"
c.Data["CommitRepoLink"] = c.Repo.RepoLink c.Data["CommitRepoLink"] = c.Repo.RepoLink
c.Data["Commits"] = commits c.Data["Commits"] = db.ValidateCommitsWithEmails(commits)
c.Data["CommitsCount"] = commits.Len() c.Data["CommitsCount"] = len(commits)
c.Data["BeforeCommitID"] = beforeCommitID c.Data["BeforeCommitID"] = beforeCommitID
c.Data["AfterCommitID"] = afterCommitID c.Data["AfterCommitID"] = afterCommitID
c.Data["Username"] = userName c.Data["Username"] = userName

View File

@@ -6,32 +6,26 @@ package repo
import ( import (
"fmt" "fmt"
"io"
"net/http" "net/http"
"path" "path"
"github.com/gogs/git-module" "github.com/gogs/git-module"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
) )
func serveData(c *context.Context, name string, r io.Reader) error { func serveData(c *context.Context, name string, data []byte) error {
buf := make([]byte, 1024) commit, err := c.Repo.Commit.CommitByPath(git.CommitByRevisionOptions{Path: c.Repo.TreePath})
n, _ := r.Read(buf)
if n >= 0 {
buf = buf[:n]
}
commit, err := c.Repo.Commit.GetCommitByPath(c.Repo.TreePath)
if err != nil { if err != nil {
return fmt.Errorf("GetCommitByPath: %v", err) return fmt.Errorf("get commit by path %q: %v", c.Repo.TreePath, err)
} }
c.Resp.Header().Set("Last-Modified", commit.Committer.When.Format(http.TimeFormat)) c.Resp.Header().Set("Last-Modified", commit.Committer.When.Format(http.TimeFormat))
if !tool.IsTextFile(buf) { if !tool.IsTextFile(data) {
if !tool.IsImageFile(buf) { if !tool.IsImageFile(data) {
c.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"") c.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"")
c.Resp.Header().Set("Content-Transfer-Encoding", "binary") c.Resp.Header().Set("Content-Transfer-Encoding", "binary")
} }
@@ -39,33 +33,30 @@ func serveData(c *context.Context, name string, r io.Reader) error {
c.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8") c.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
} }
if _, err := c.Resp.Write(buf); err != nil { if _, err := c.Resp.Write(data); err != nil {
return fmt.Errorf("write buffer to response: %v", err) return fmt.Errorf("write buffer to response: %v", err)
} }
_, err = io.Copy(c.Resp, r) return nil
return err
} }
func ServeBlob(c *context.Context, blob *git.Blob) error { func ServeBlob(c *context.Context, blob *git.Blob) error {
dataRc, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
return err return err
} }
return serveData(c, path.Base(c.Repo.TreePath), dataRc) return serveData(c, path.Base(c.Repo.TreePath), p)
} }
func SingleDownload(c *context.Context) { func SingleDownload(c *context.Context) {
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) blob, err := c.Repo.Commit.Blob(c.Repo.TreePath)
if err != nil { if err != nil {
if git.IsErrNotExist(err) { c.NotFoundOrServerError("get blob", gitutil.IsErrRevisionNotExist, err)
c.Handle(404, "GetBlobByPath", nil)
} else {
c.Handle(500, "GetBlobByPath", err)
}
return return
} }
if err = ServeBlob(c, blob); err != nil { if err = ServeBlob(c, blob); err != nil {
c.Handle(500, "ServeBlob", err) c.ServerError("serve blob", err)
return
} }
} }

View File

@@ -6,19 +6,18 @@ package repo
import ( import (
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"path" "path"
"strings" "strings"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/pathutil" "gogs.io/gogs/internal/pathutil"
"gogs.io/gogs/internal/template" "gogs.io/gogs/internal/template"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
@@ -55,20 +54,20 @@ func editFile(c *context.Context, isNewFile bool) {
treeNames, treePaths := getParentTreeFields(c.Repo.TreePath) treeNames, treePaths := getParentTreeFields(c.Repo.TreePath)
if !isNewFile { if !isNewFile {
entry, err := c.Repo.Commit.GetTreeEntryByPath(c.Repo.TreePath) entry, err := c.Repo.Commit.TreeEntry(c.Repo.TreePath)
if err != nil { if err != nil {
c.NotFoundOrServerError("GetTreeEntryByPath", git.IsErrNotExist, err) c.NotFoundOrServerError("get tree entry", gitutil.IsErrRevisionNotExist, err)
return return
} }
// No way to edit a directory online. // No way to edit a directory online.
if entry.IsDir() { if entry.IsTree() {
c.NotFound() c.NotFound()
return return
} }
blob := entry.Blob() blob := entry.Blob()
dataRc, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
c.ServerError("blob.Data", err) c.ServerError("blob.Data", err)
return return
@@ -77,23 +76,17 @@ func editFile(c *context.Context, isNewFile bool) {
c.Data["FileSize"] = blob.Size() c.Data["FileSize"] = blob.Size()
c.Data["FileName"] = blob.Name() c.Data["FileName"] = blob.Name()
buf := make([]byte, 1024)
n, _ := dataRc.Read(buf)
buf = buf[:n]
// Only text file are editable online. // Only text file are editable online.
if !tool.IsTextFile(buf) { if !tool.IsTextFile(p) {
c.NotFound() c.NotFound()
return return
} }
d, _ := ioutil.ReadAll(dataRc) if err, content := template.ToUTF8WithErr(p); err != nil {
buf = append(buf, d...)
if err, content := template.ToUTF8WithErr(buf); err != nil {
if err != nil { if err != nil {
log.Error("Failed to convert encoding to UTF-8: %v", err) log.Error("Failed to convert encoding to UTF-8: %v", err)
} }
c.Data["FileContent"] = string(buf) c.Data["FileContent"] = string(p)
} else { } else {
c.Data["FileContent"] = content c.Data["FileContent"] = content
} }
@@ -182,9 +175,9 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
var newTreePath string var newTreePath string
for index, part := range treeNames { for index, part := range treeNames {
newTreePath = path.Join(newTreePath, part) newTreePath = path.Join(newTreePath, part)
entry, err := c.Repo.Commit.GetTreeEntryByPath(newTreePath) entry, err := c.Repo.Commit.TreeEntry(newTreePath)
if err != nil { if err != nil {
if git.IsErrNotExist(err) { if gitutil.IsErrRevisionNotExist(err) {
// Means there is no item with that name, so we're good // Means there is no item with that name, so we're good
break break
} }
@@ -193,17 +186,17 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
return return
} }
if index != len(treeNames)-1 { if index != len(treeNames)-1 {
if !entry.IsDir() { if !entry.IsTree() {
c.FormErr("TreePath") c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), EDIT_FILE, &f) c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), EDIT_FILE, &f)
return return
} }
} else { } else {
if entry.IsLink() { if entry.IsSymlink() {
c.FormErr("TreePath") c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), EDIT_FILE, &f) c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), EDIT_FILE, &f)
return return
} else if entry.IsDir() { } else if entry.IsTree() {
c.FormErr("TreePath") c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), EDIT_FILE, &f) c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), EDIT_FILE, &f)
return return
@@ -212,9 +205,9 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
} }
if !isNewFile { if !isNewFile {
_, err := c.Repo.Commit.GetTreeEntryByPath(oldTreePath) _, err := c.Repo.Commit.TreeEntry(oldTreePath)
if err != nil { if err != nil {
if git.IsErrNotExist(err) { if gitutil.IsErrRevisionNotExist(err) {
c.FormErr("TreePath") c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), EDIT_FILE, &f) c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), EDIT_FILE, &f)
} else { } else {
@@ -223,7 +216,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
return return
} }
if lastCommit != c.Repo.CommitID { if lastCommit != c.Repo.CommitID {
files, err := c.Repo.Commit.GetFilesChangedSinceCommit(lastCommit) files, err := c.Repo.Commit.FilesChangedAfter(lastCommit)
if err != nil { if err != nil {
c.ServerError("GetFilesChangedSinceCommit", err) c.ServerError("GetFilesChangedSinceCommit", err)
return return
@@ -240,9 +233,9 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
if oldTreePath != f.TreePath { if oldTreePath != f.TreePath {
// We have a new filename (rename or completely new file) so we need to make sure it doesn't already exist, can't clobber. // We have a new filename (rename or completely new file) so we need to make sure it doesn't already exist, can't clobber.
entry, err := c.Repo.Commit.GetTreeEntryByPath(f.TreePath) entry, err := c.Repo.Commit.TreeEntry(f.TreePath)
if err != nil { if err != nil {
if !git.IsErrNotExist(err) { if !gitutil.IsErrRevisionNotExist(err) {
c.ServerError("GetTreeEntryByPath", err) c.ServerError("GetTreeEntryByPath", err)
return return
} }
@@ -302,11 +295,11 @@ func NewFilePost(c *context.Context, f form.EditRepoFile) {
func DiffPreviewPost(c *context.Context, f form.EditPreviewDiff) { func DiffPreviewPost(c *context.Context, f form.EditPreviewDiff) {
treePath := c.Repo.TreePath treePath := c.Repo.TreePath
entry, err := c.Repo.Commit.GetTreeEntryByPath(treePath) entry, err := c.Repo.Commit.TreeEntry(treePath)
if err != nil { if err != nil {
c.Error(500, "GetTreeEntryByPath: "+err.Error()) c.Error(500, "GetTreeEntryByPath: "+err.Error())
return return
} else if entry.IsDir() { } else if entry.IsTree() {
c.Error(422) c.Error(422)
return return
} }
@@ -468,9 +461,9 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
var newTreePath string var newTreePath string
for _, part := range treeNames { for _, part := range treeNames {
newTreePath = path.Join(newTreePath, part) newTreePath = path.Join(newTreePath, part)
entry, err := c.Repo.Commit.GetTreeEntryByPath(newTreePath) entry, err := c.Repo.Commit.TreeEntry(newTreePath)
if err != nil { if err != nil {
if git.IsErrNotExist(err) { if gitutil.IsErrRevisionNotExist(err) {
// Means there is no item with that name, so we're good // Means there is no item with that name, so we're good
break break
} }
@@ -480,7 +473,7 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
} }
// User can only upload files to a directory. // User can only upload files to a directory.
if !entry.IsDir() { if !entry.IsTree() {
c.FormErr("TreePath") c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), UPLOAD_FILE, &f) c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), UPLOAD_FILE, &f)
return return

View File

@@ -6,8 +6,6 @@ package repo
import ( import (
"fmt" "fmt"
"io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@@ -303,30 +301,23 @@ func RetrieveRepoMetas(c *context.Context, repo *db.Repository) []*db.Label {
} }
func getFileContentFromDefaultBranch(c *context.Context, filename string) (string, bool) { func getFileContentFromDefaultBranch(c *context.Context, filename string) (string, bool) {
var r io.Reader
var bytes []byte
if c.Repo.Commit == nil { if c.Repo.Commit == nil {
var err error var err error
c.Repo.Commit, err = c.Repo.GitRepo.GetBranchCommit(c.Repo.Repository.DefaultBranch) c.Repo.Commit, err = c.Repo.GitRepo.BranchCommit(c.Repo.Repository.DefaultBranch)
if err != nil { if err != nil {
return "", false return "", false
} }
} }
entry, err := c.Repo.Commit.GetTreeEntryByPath(filename) entry, err := c.Repo.Commit.TreeEntry(filename)
if err != nil { if err != nil {
return "", false return "", false
} }
r, err = entry.Blob().Data() p, err := entry.Blob().Bytes()
if err != nil { if err != nil {
return "", false return "", false
} }
bytes, err = ioutil.ReadAll(r) return string(p), true
if err != nil {
return "", false
}
return string(bytes), true
} }
func setTemplateIfExists(c *context.Context, ctxDataKey string, possibleFiles []string) { func setTemplateIfExists(c *context.Context, ctxDataKey string, possibleFiles []string) {
@@ -656,7 +647,7 @@ func viewIssue(c *context.Context, isPullList bool) {
} }
c.Data["IsPullBranchDeletable"] = pull.BaseRepoID == pull.HeadRepoID && c.Data["IsPullBranchDeletable"] = pull.BaseRepoID == pull.HeadRepoID &&
c.Repo.IsWriter() && c.Repo.GitRepo.IsBranchExist(pull.HeadBranch) && c.Repo.IsWriter() && c.Repo.GitRepo.HasBranch(pull.HeadBranch) &&
!branchProtected !branchProtected
c.Data["DeleteBranchLink"] = c.Repo.MakeURL(url.URL{ c.Data["DeleteBranchLink"] = c.Repo.MakeURL(url.URL{

View File

@@ -5,7 +5,6 @@
package repo package repo
import ( import (
"container/list"
"path" "path"
"strings" "strings"
@@ -19,6 +18,7 @@ import (
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
) )
@@ -183,19 +183,21 @@ func PrepareMergedViewPullInfo(c *context.Context, issue *db.Issue) {
c.Data["BaseTarget"] = c.Repo.Owner.Name + "/" + pull.BaseBranch c.Data["BaseTarget"] = c.Repo.Owner.Name + "/" + pull.BaseBranch
var err error var err error
c.Data["NumCommits"], err = c.Repo.GitRepo.CommitsCountBetween(pull.MergeBase, pull.MergedCommitID) c.Data["NumCommits"], err = c.Repo.GitRepo.RevListCount([]string{pull.MergeBase + "..." + pull.MergedCommitID})
if err != nil { if err != nil {
c.ServerError("Repo.GitRepo.CommitsCountBetween", err) c.ServerError("Repo.GitRepo.CommitsCountBetween", err)
return return
} }
c.Data["NumFiles"], err = c.Repo.GitRepo.FilesCountBetween(pull.MergeBase, pull.MergedCommitID)
names, err := c.Repo.GitRepo.DiffNameOnly(pull.MergeBase, pull.MergedCommitID, git.DiffNameOnlyOptions{NeedsMergeBase: true})
c.Data["NumFiles"] = len(names)
if err != nil { if err != nil {
c.ServerError("Repo.GitRepo.FilesCountBetween", err) c.ServerError("Repo.GitRepo.FilesCountBetween", err)
return return
} }
} }
func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestInfo { func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *gitutil.PullRequestMeta {
repo := c.Repo.Repository repo := c.Repo.Repository
pull := issue.PullRequest pull := issue.PullRequest
@@ -208,14 +210,14 @@ func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestIn
) )
if pull.HeadRepo != nil { if pull.HeadRepo != nil {
headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath()) headGitRepo, err = git.Open(pull.HeadRepo.RepoPath())
if err != nil { if err != nil {
c.ServerError("OpenRepository", err) c.ServerError("open repository", err)
return nil return nil
} }
} }
if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBranch) { if pull.HeadRepo == nil || !headGitRepo.HasBranch(pull.HeadBranch) {
c.Data["IsPullReuqestBroken"] = true c.Data["IsPullReuqestBroken"] = true
c.Data["HeadTarget"] = "deleted" c.Data["HeadTarget"] = "deleted"
c.Data["NumCommits"] = 0 c.Data["NumCommits"] = 0
@@ -223,8 +225,8 @@ func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestIn
return nil return nil
} }
prInfo, err := headGitRepo.GetPullRequestInfo(db.RepoPath(repo.Owner.Name, repo.Name), baseRepoPath := db.RepoPath(repo.Owner.Name, repo.Name)
pull.BaseBranch, pull.HeadBranch) prMeta, err := gitutil.Module.PullRequestMeta(headGitRepo.Path(), baseRepoPath, pull.HeadBranch, pull.BaseBranch)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") { if strings.Contains(err.Error(), "fatal: Not a valid object name") {
c.Data["IsPullReuqestBroken"] = true c.Data["IsPullReuqestBroken"] = true
@@ -237,9 +239,9 @@ func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestIn
c.ServerError("GetPullRequestInfo", err) c.ServerError("GetPullRequestInfo", err)
return nil return nil
} }
c.Data["NumCommits"] = prInfo.Commits.Len() c.Data["NumCommits"] = len(prMeta.Commits)
c.Data["NumFiles"] = prInfo.NumFiles c.Data["NumFiles"] = prMeta.NumFiles
return prInfo return prMeta
} }
func ViewPullCommits(c *context.Context) { func ViewPullCommits(c *context.Context) {
@@ -257,25 +259,25 @@ func ViewPullCommits(c *context.Context) {
c.Data["Reponame"] = pull.HeadRepo.Name c.Data["Reponame"] = pull.HeadRepo.Name
} }
var commits *list.List var commits []*git.Commit
if pull.HasMerged { if pull.HasMerged {
PrepareMergedViewPullInfo(c, issue) PrepareMergedViewPullInfo(c, issue)
if c.Written() { if c.Written() {
return return
} }
startCommit, err := c.Repo.GitRepo.GetCommit(pull.MergeBase) startCommit, err := c.Repo.GitRepo.CatFileCommit(pull.MergeBase)
if err != nil { if err != nil {
c.ServerError("Repo.GitRepo.GetCommit", err) c.ServerError("get commit of merge base", err)
return return
} }
endCommit, err := c.Repo.GitRepo.GetCommit(pull.MergedCommitID) endCommit, err := c.Repo.GitRepo.CatFileCommit(pull.MergedCommitID)
if err != nil { if err != nil {
c.ServerError("Repo.GitRepo.GetCommit", err) c.ServerError("get merged commit", err)
return return
} }
commits, err = c.Repo.GitRepo.CommitsBetween(endCommit, startCommit) commits, err = c.Repo.GitRepo.RevList([]string{startCommit.ID.String() + "..." + endCommit.ID.String()})
if err != nil { if err != nil {
c.ServerError("Repo.GitRepo.CommitsBetween", err) c.ServerError("list commits", err)
return return
} }
@@ -290,9 +292,8 @@ func ViewPullCommits(c *context.Context) {
commits = prInfo.Commits commits = prInfo.Commits
} }
commits = db.ValidateCommitsWithEmails(commits) c.Data["Commits"] = db.ValidateCommitsWithEmails(commits)
c.Data["Commits"] = commits c.Data["CommitsCount"] = len(commits)
c.Data["CommitsCount"] = commits.Len()
c.Success(PULL_COMMITS) c.Success(PULL_COMMITS)
} }
@@ -308,7 +309,7 @@ func ViewPullFiles(c *context.Context) {
pull := issue.PullRequest pull := issue.PullRequest
var ( var (
diffRepoPath string diffGitRepo *git.Repository
startCommitID string startCommitID string
endCommitID string endCommitID string
gitRepo *git.Repository gitRepo *git.Repository
@@ -320,7 +321,7 @@ func ViewPullFiles(c *context.Context) {
return return
} }
diffRepoPath = c.Repo.GitRepo.Path diffGitRepo = c.Repo.GitRepo
startCommitID = pull.MergeBase startCommitID = pull.MergeBase
endCommitID = pull.MergedCommitID endCommitID = pull.MergedCommitID
gitRepo = c.Repo.GitRepo gitRepo = c.Repo.GitRepo
@@ -335,37 +336,38 @@ func ViewPullFiles(c *context.Context) {
headRepoPath := db.RepoPath(pull.HeadUserName, pull.HeadRepo.Name) headRepoPath := db.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
headGitRepo, err := git.OpenRepository(headRepoPath) headGitRepo, err := git.Open(headRepoPath)
if err != nil { if err != nil {
c.ServerError("OpenRepository", err) c.ServerError("open repository", err)
return return
} }
headCommitID, err := headGitRepo.GetBranchCommitID(pull.HeadBranch) headCommitID, err := headGitRepo.BranchCommitID(pull.HeadBranch)
if err != nil { if err != nil {
c.ServerError("GetBranchCommitID", err) c.ServerError("get head branch commit ID", err)
return return
} }
diffRepoPath = headRepoPath diffGitRepo = headGitRepo
startCommitID = prInfo.MergeBase startCommitID = prInfo.MergeBase
endCommitID = headCommitID endCommitID = headCommitID
gitRepo = headGitRepo gitRepo = headGitRepo
} }
diff, err := db.GetDiffRange(diffRepoPath, diff, err := gitutil.RepoDiff(diffGitRepo,
startCommitID, endCommitID, conf.Git.MaxGitDiffLines, endCommitID, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars,
conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles) git.DiffOptions{Base: startCommitID},
)
if err != nil { if err != nil {
c.ServerError("GetDiffRange", err) c.ServerError("get diff", err)
return return
} }
c.Data["Diff"] = diff c.Data["Diff"] = diff
c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 c.Data["DiffNotAvailable"] = diff.NumFiles() == 0
commit, err := gitRepo.GetCommit(endCommitID) commit, err := gitRepo.CatFileCommit(endCommitID)
if err != nil { if err != nil {
c.ServerError("GetCommit", err) c.ServerError("get commit", err)
return return
} }
@@ -424,7 +426,7 @@ func MergePullRequest(c *context.Context) {
c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index)) c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
} }
func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Repository, *git.PullRequestInfo, string, string) { func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Repository, *gitutil.PullRequestMeta, string, string) {
baseRepo := c.Repo.Repository baseRepo := c.Repo.Repository
// Get compared branches information // Get compared branches information
@@ -473,7 +475,7 @@ func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Reposi
c.Repo.PullRequest.SameRepo = isSameRepo c.Repo.PullRequest.SameRepo = isSameRepo
// Check if base branch is valid. // Check if base branch is valid.
if !c.Repo.GitRepo.IsBranchExist(baseBranch) { if !c.Repo.GitRepo.HasBranch(baseBranch) {
c.NotFound() c.NotFound()
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
@@ -497,9 +499,9 @@ func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Reposi
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
headGitRepo, err = git.OpenRepository(db.RepoPath(headUser.Name, headRepo.Name)) headGitRepo, err = git.Open(db.RepoPath(headUser.Name, headRepo.Name))
if err != nil { if err != nil {
c.ServerError("OpenRepository", err) c.ServerError("open repository", err)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
} else { } else {
@@ -514,31 +516,32 @@ func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Reposi
} }
// Check if head branch is valid. // Check if head branch is valid.
if !headGitRepo.IsBranchExist(headBranch) { if !headGitRepo.HasBranch(headBranch) {
c.NotFound() c.NotFound()
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
headBranches, err := headGitRepo.GetBranches() headBranches, err := headGitRepo.Branches()
if err != nil { if err != nil {
c.ServerError("GetBranches", err) c.ServerError("get branches", err)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
c.Data["HeadBranches"] = headBranches c.Data["HeadBranches"] = headBranches
prInfo, err := headGitRepo.GetPullRequestInfo(db.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) baseRepoPath := db.RepoPath(baseRepo.Owner.Name, baseRepo.Name)
meta, err := gitutil.Module.PullRequestMeta(headGitRepo.Path(), baseRepoPath, headBranch, baseBranch)
if err != nil { if err != nil {
if git.IsErrNoMergeBase(err) { if gitutil.IsErrNoMergeBase(err) {
c.Data["IsNoMergeBase"] = true c.Data["IsNoMergeBase"] = true
c.Success(COMPARE_PULL) c.Success(COMPARE_PULL)
} else { } else {
c.ServerError("GetPullRequestInfo", err) c.ServerError("get pull request meta", err)
} }
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
c.Data["BeforeCommitID"] = prInfo.MergeBase c.Data["BeforeCommitID"] = meta.MergeBase
return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch return headUser, headRepo, headGitRepo, meta, baseBranch, headBranch
} }
func PrepareCompareDiff( func PrepareCompareDiff(
@@ -546,8 +549,9 @@ func PrepareCompareDiff(
headUser *db.User, headUser *db.User,
headRepo *db.Repository, headRepo *db.Repository,
headGitRepo *git.Repository, headGitRepo *git.Repository,
prInfo *git.PullRequestInfo, meta *gitutil.PullRequestMeta,
baseBranch, headBranch string) bool { headBranch string,
) bool {
var ( var (
repo = c.Repo.Repository repo = c.Repo.Repository
@@ -557,44 +561,44 @@ func PrepareCompareDiff(
// Get diff information. // Get diff information.
c.Data["CommitRepoLink"] = headRepo.Link() c.Data["CommitRepoLink"] = headRepo.Link()
headCommitID, err := headGitRepo.GetBranchCommitID(headBranch) headCommitID, err := headGitRepo.BranchCommitID(headBranch)
if err != nil { if err != nil {
c.ServerError("GetBranchCommitID", err) c.ServerError("get head branch commit ID", err)
return false return false
} }
c.Data["AfterCommitID"] = headCommitID c.Data["AfterCommitID"] = headCommitID
if headCommitID == prInfo.MergeBase { if headCommitID == meta.MergeBase {
c.Data["IsNothingToCompare"] = true c.Data["IsNothingToCompare"] = true
return true return true
} }
diff, err := db.GetDiffRange(db.RepoPath(headUser.Name, headRepo.Name), diff, err := gitutil.RepoDiff(headGitRepo,
prInfo.MergeBase, headCommitID, conf.Git.MaxGitDiffLines, headCommitID, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars,
conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles) git.DiffOptions{Base: meta.MergeBase},
)
if err != nil { if err != nil {
c.ServerError("GetDiffRange", err) c.ServerError("get repository diff", err)
return false return false
} }
c.Data["Diff"] = diff c.Data["Diff"] = diff
c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 c.Data["DiffNotAvailable"] = diff.NumFiles() == 0
headCommit, err := headGitRepo.GetCommit(headCommitID) headCommit, err := headGitRepo.CatFileCommit(headCommitID)
if err != nil { if err != nil {
c.ServerError("GetCommit", err) c.ServerError("get head commit", err)
return false return false
} }
prInfo.Commits = db.ValidateCommitsWithEmails(prInfo.Commits) c.Data["Commits"] = db.ValidateCommitsWithEmails(meta.Commits)
c.Data["Commits"] = prInfo.Commits c.Data["CommitCount"] = len(meta.Commits)
c.Data["CommitCount"] = prInfo.Commits.Len()
c.Data["Username"] = headUser.Name c.Data["Username"] = headUser.Name
c.Data["Reponame"] = headRepo.Name c.Data["Reponame"] = headRepo.Name
c.Data["IsImageFile"] = headCommit.IsImageFile c.Data["IsImageFile"] = headCommit.IsImageFile
headTarget := path.Join(headUser.Name, repo.Name) headTarget := path.Join(headUser.Name, repo.Name)
c.Data["SourcePath"] = conf.Server.Subpath + "/" + path.Join(headTarget, "src", headCommitID) c.Data["SourcePath"] = conf.Server.Subpath + "/" + path.Join(headTarget, "src", headCommitID)
c.Data["BeforeSourcePath"] = conf.Server.Subpath + "/" + path.Join(headTarget, "src", prInfo.MergeBase) c.Data["BeforeSourcePath"] = conf.Server.Subpath + "/" + path.Join(headTarget, "src", meta.MergeBase)
c.Data["RawPath"] = conf.Server.Subpath + "/" + path.Join(headTarget, "raw", headCommitID) c.Data["RawPath"] = conf.Server.Subpath + "/" + path.Join(headTarget, "raw", headCommitID)
return false return false
} }
@@ -625,7 +629,7 @@ func CompareAndPullRequest(c *context.Context) {
return return
} }
nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, headBranch)
if c.Written() { if c.Written() {
return return
} }
@@ -667,7 +671,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
attachments []string attachments []string
) )
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(c) headUser, headRepo, headGitRepo, meta, baseBranch, headBranch := ParseCompareInfo(c)
if c.Written() { if c.Written() {
return return
} }
@@ -686,7 +690,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
// This stage is already stop creating new pull request, so it does not matter if it has // This stage is already stop creating new pull request, so it does not matter if it has
// something to compare or not. // something to compare or not.
PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) PrepareCompareDiff(c, headUser, headRepo, headGitRepo, meta, headBranch)
if c.Written() { if c.Written() {
return return
} }
@@ -695,9 +699,9 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
return return
} }
patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch) patch, err := headGitRepo.DiffBinary(meta.MergeBase, headBranch)
if err != nil { if err != nil {
c.ServerError("GetPatch", err) c.ServerError("get patch", err)
return return
} }
@@ -720,7 +724,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
BaseBranch: baseBranch, BaseBranch: baseBranch,
HeadRepo: headRepo, HeadRepo: headRepo,
BaseRepo: repo, BaseRepo: repo,
MergeBase: prInfo.MergeBase, MergeBase: meta.MergeBase,
Type: db.PULL_REQUEST_GOGS, Type: db.PULL_REQUEST_GOGS,
} }
// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt // FIXME: check error in the case two people send pull request at almost same time, give nice error prompt

View File

@@ -8,13 +8,15 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/gogs/git-module"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/conf"
) )
const ( const (
@@ -26,14 +28,14 @@ const (
func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *db.Release, countCache map[string]int64) error { func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *db.Release, countCache map[string]int64) error {
// Get count if not exists // Get count if not exists
if _, ok := countCache[release.Target]; !ok { if _, ok := countCache[release.Target]; !ok {
if repoCtx.GitRepo.IsBranchExist(release.Target) { if repoCtx.GitRepo.HasBranch(release.Target) {
commit, err := repoCtx.GitRepo.GetBranchCommit(release.Target) commit, err := repoCtx.GitRepo.BranchCommit(release.Target)
if err != nil { if err != nil {
return fmt.Errorf("GetBranchCommit: %v", err) return fmt.Errorf("get branch commit: %v", err)
} }
countCache[release.Target], err = commit.CommitsCount() countCache[release.Target], err = commit.CommitsCount()
if err != nil { if err != nil {
return fmt.Errorf("CommitsCount: %v", err) return fmt.Errorf("count commits: %v", err)
} }
} else { } else {
// Use NumCommits of the newest release on that target // Use NumCommits of the newest release on that target
@@ -49,13 +51,13 @@ func Releases(c *context.Context) {
c.Data["PageIsViewFiles"] = true c.Data["PageIsViewFiles"] = true
c.Data["PageIsReleaseList"] = true c.Data["PageIsReleaseList"] = true
tagsResult, err := c.Repo.GitRepo.GetTagsAfter(c.Query("after"), 10) tagsPage, err := gitutil.Module.ListTagsAfter(c.Repo.GitRepo.Path(), c.Query("after"), 10)
if err != nil { if err != nil {
c.Handle(500, fmt.Sprintf("GetTags '%s'", c.Repo.Repository.RepoPath()), err) c.ServerError("get tags", err)
return return
} }
releases, err := db.GetPublishedReleasesByRepoID(c.Repo.Repository.ID, tagsResult.Tags...) releases, err := db.GetPublishedReleasesByRepoID(c.Repo.Repository.ID, tagsPage.Tags...)
if err != nil { if err != nil {
c.Handle(500, "GetPublishedReleasesByRepoID", err) c.Handle(500, "GetPublishedReleasesByRepoID", err)
return return
@@ -64,8 +66,8 @@ func Releases(c *context.Context) {
// Temproray cache commits count of used branches to speed up. // Temproray cache commits count of used branches to speed up.
countCache := make(map[string]int64) countCache := make(map[string]int64)
results := make([]*db.Release, len(tagsResult.Tags)) results := make([]*db.Release, len(tagsPage.Tags))
for i, rawTag := range tagsResult.Tags { for i, rawTag := range tagsPage.Tags {
for j, r := range releases { for j, r := range releases {
if r == nil || r.TagName != rawTag { if r == nil || r.TagName != rawTag {
continue continue
@@ -89,9 +91,9 @@ func Releases(c *context.Context) {
// No published release matches this tag // No published release matches this tag
if results[i] == nil { if results[i] == nil {
commit, err := c.Repo.GitRepo.GetTagCommit(rawTag) commit, err := c.Repo.GitRepo.TagCommit(rawTag)
if err != nil { if err != nil {
c.Handle(500, "GetTagCommit", err) c.Handle(500, "get tag commit", err)
return return
} }
@@ -103,7 +105,7 @@ func Releases(c *context.Context) {
results[i].NumCommits, err = commit.CommitsCount() results[i].NumCommits, err = commit.CommitsCount()
if err != nil { if err != nil {
c.Handle(500, "CommitsCount", err) c.ServerError("count commits", err)
return return
} }
results[i].NumCommitsBehind = c.Repo.CommitsCount - results[i].NumCommits results[i].NumCommitsBehind = c.Repo.CommitsCount - results[i].NumCommits
@@ -113,7 +115,7 @@ func Releases(c *context.Context) {
// Only show drafts if user is viewing the latest page // Only show drafts if user is viewing the latest page
var drafts []*db.Release var drafts []*db.Release
if tagsResult.HasLatest { if tagsPage.HasLatest {
drafts, err = db.GetDraftReleasesByRepoID(c.Repo.Repository.ID) drafts, err = db.GetDraftReleasesByRepoID(c.Repo.Repository.ID)
if err != nil { if err != nil {
c.Handle(500, "GetDraftReleasesByRepoID", err) c.Handle(500, "GetDraftReleasesByRepoID", err)
@@ -140,9 +142,9 @@ func Releases(c *context.Context) {
} }
c.Data["Releases"] = results c.Data["Releases"] = results
c.Data["HasPrevious"] = !tagsResult.HasLatest c.Data["HasPrevious"] = !tagsPage.HasLatest
c.Data["ReachEnd"] = tagsResult.ReachEnd c.Data["ReachEnd"] = !tagsPage.HasNext
c.Data["PreviousAfter"] = tagsResult.PreviousAfter c.Data["PreviousAfter"] = tagsPage.PreviousAfter
if len(results) > 0 { if len(results) > 0 {
c.Data["NextAfter"] = results[len(results)-1].TagName c.Data["NextAfter"] = results[len(results)-1].TagName
} }
@@ -175,14 +177,14 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
return return
} }
if !c.Repo.GitRepo.IsBranchExist(f.Target) { if !c.Repo.GitRepo.HasBranch(f.Target) {
c.RenderWithErr(c.Tr("form.target_branch_not_exist"), RELEASE_NEW, &f) c.RenderWithErr(c.Tr("form.target_branch_not_exist"), RELEASE_NEW, &f)
return return
} }
// Use current time if tag not yet exist, otherwise get time from Git // Use current time if tag not yet exist, otherwise get time from Git
var tagCreatedUnix int64 var tagCreatedUnix int64
tag, err := c.Repo.GitRepo.GetTag(f.TagName) tag, err := c.Repo.GitRepo.Tag(git.RefsTags + f.TagName)
if err == nil { if err == nil {
commit, err := tag.Commit() commit, err := tag.Commit()
if err == nil { if err == nil {
@@ -190,15 +192,15 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
} }
} }
commit, err := c.Repo.GitRepo.GetBranchCommit(f.Target) commit, err := c.Repo.GitRepo.BranchCommit(f.Target)
if err != nil { if err != nil {
c.Handle(500, "GetBranchCommit", err) c.ServerError("get branch commit", err)
return return
} }
commitsCount, err := commit.CommitsCount() commitsCount, err := commit.CommitsCount()
if err != nil { if err != nil {
c.Handle(500, "CommitsCount", err) c.ServerError("count commits", err)
return return
} }

View File

@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"path/filepath"
"strings" "strings"
"github.com/unknwon/com" "github.com/unknwon/com"
@@ -275,18 +276,18 @@ func Download(c *context.Context) {
refName string refName string
ext string ext string
archivePath string archivePath string
archiveType git.ArchiveType archiveFormat git.ArchiveFormat
) )
switch { switch {
case strings.HasSuffix(uri, ".zip"): case strings.HasSuffix(uri, ".zip"):
ext = ".zip" ext = ".zip"
archivePath = path.Join(c.Repo.GitRepo.Path, "archives/zip") archivePath = filepath.Join(c.Repo.GitRepo.Path(), "archives", "zip")
archiveType = git.ZIP archiveFormat = git.ArchiveZip
case strings.HasSuffix(uri, ".tar.gz"): case strings.HasSuffix(uri, ".tar.gz"):
ext = ".tar.gz" ext = ".tar.gz"
archivePath = path.Join(c.Repo.GitRepo.Path, "archives/targz") archivePath = filepath.Join(c.Repo.GitRepo.Path(), "archives", "targz")
archiveType = git.TARGZ archiveFormat = git.ArchiveTarGz
default: default:
log.Trace("Unknown format: %s", uri) log.Trace("Unknown format: %s", uri)
c.Error(404) c.Error(404)
@@ -307,20 +308,20 @@ func Download(c *context.Context) {
err error err error
) )
gitRepo := c.Repo.GitRepo gitRepo := c.Repo.GitRepo
if gitRepo.IsBranchExist(refName) { if gitRepo.HasBranch(refName) {
commit, err = gitRepo.GetBranchCommit(refName) commit, err = gitRepo.BranchCommit(refName)
if err != nil { if err != nil {
c.Handle(500, "GetBranchCommit", err) c.ServerError("get branch commit", err)
return return
} }
} else if gitRepo.IsTagExist(refName) { } else if gitRepo.HasTag(refName) {
commit, err = gitRepo.GetTagCommit(refName) commit, err = gitRepo.TagCommit(refName)
if err != nil { if err != nil {
c.Handle(500, "GetTagCommit", err) c.ServerError("get tag commit", err)
return return
} }
} else if len(refName) >= 7 && len(refName) <= 40 { } else if len(refName) >= 7 && len(refName) <= 40 {
commit, err = gitRepo.GetCommit(refName) commit, err = gitRepo.CatFileCommit(refName)
if err != nil { if err != nil {
c.NotFound() c.NotFound()
return return
@@ -332,8 +333,8 @@ func Download(c *context.Context) {
archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext) archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext)
if !com.IsFile(archivePath) { if !com.IsFile(archivePath) {
if err := commit.CreateArchive(archivePath, archiveType); err != nil { if err := commit.CreateArchive(archiveFormat, archivePath); err != nil {
c.Handle(500, "Download -> CreateArchive "+archivePath, err) c.ServerError("creates archive", err)
return return
} }
} }

View File

@@ -7,6 +7,7 @@ package repo
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"strings" "strings"
"time" "time"
@@ -448,7 +449,7 @@ func SettingsBranches(c *context.Context) {
// Filter out deleted branches // Filter out deleted branches
branches := make([]string, 0, len(protectBranches)) branches := make([]string, 0, len(protectBranches))
for i := range protectBranches { for i := range protectBranches {
if c.Repo.GitRepo.IsBranchExist(protectBranches[i].Name) { if c.Repo.GitRepo.HasBranch(protectBranches[i].Name) {
branches = append(branches, protectBranches[i].Name) branches = append(branches, protectBranches[i].Name)
} }
} }
@@ -459,15 +460,12 @@ func SettingsBranches(c *context.Context) {
func UpdateDefaultBranch(c *context.Context) { func UpdateDefaultBranch(c *context.Context) {
branch := c.Query("branch") branch := c.Query("branch")
if c.Repo.GitRepo.IsBranchExist(branch) && if c.Repo.GitRepo.HasBranch(branch) &&
c.Repo.Repository.DefaultBranch != branch { c.Repo.Repository.DefaultBranch != branch {
c.Repo.Repository.DefaultBranch = branch c.Repo.Repository.DefaultBranch = branch
if err := c.Repo.GitRepo.SetDefaultBranch(branch); err != nil { if _, err := c.Repo.GitRepo.SymbolicRef(git.SymbolicRefOptions{
if !git.IsErrUnsupportedVersion(err) { Ref: git.RefsHeads + branch,
c.Handle(500, "SetDefaultBranch", err) }); err != nil {
return
}
c.Flash.Warning(c.Tr("repo.settings.update_default_branch_unsupported")) c.Flash.Warning(c.Tr("repo.settings.update_default_branch_unsupported"))
c.Redirect(c.Repo.RepoLink + "/settings/branches") c.Redirect(c.Repo.RepoLink + "/settings/branches")
return return
@@ -485,7 +483,7 @@ func UpdateDefaultBranch(c *context.Context) {
func SettingsProtectedBranch(c *context.Context) { func SettingsProtectedBranch(c *context.Context) {
branch := c.Params("*") branch := c.Params("*")
if !c.Repo.GitRepo.IsBranchExist(branch) { if !c.Repo.GitRepo.HasBranch(branch) {
c.NotFound() c.NotFound()
return return
} }
@@ -530,7 +528,7 @@ func SettingsProtectedBranch(c *context.Context) {
func SettingsProtectedBranchPost(c *context.Context, f form.ProtectBranch) { func SettingsProtectedBranchPost(c *context.Context, f form.ProtectBranch) {
branch := c.Params("*") branch := c.Params("*")
if !c.Repo.GitRepo.IsBranchExist(branch) { if !c.Repo.GitRepo.HasBranch(branch) {
c.NotFound() c.NotFound()
return return
} }
@@ -570,7 +568,7 @@ func SettingsGitHooks(c *context.Context) {
c.Data["Title"] = c.Tr("repo.settings.githooks") c.Data["Title"] = c.Tr("repo.settings.githooks")
c.Data["PageIsSettingsGitHooks"] = true c.Data["PageIsSettingsGitHooks"] = true
hooks, err := c.Repo.GitRepo.Hooks() hooks, err := c.Repo.GitRepo.Hooks("custom_hooks")
if err != nil { if err != nil {
c.Handle(500, "Hooks", err) c.Handle(500, "Hooks", err)
return return
@@ -586,9 +584,9 @@ func SettingsGitHooksEdit(c *context.Context) {
c.Data["RequireSimpleMDE"] = true c.Data["RequireSimpleMDE"] = true
name := c.Params(":name") name := c.Params(":name")
hook, err := c.Repo.GitRepo.GetHook(name) hook, err := c.Repo.GitRepo.Hook("custom_hooks", git.HookName(name))
if err != nil { if err != nil {
if err == git.ErrNotValidHook { if err == os.ErrNotExist {
c.Handle(404, "GetHook", err) c.Handle(404, "GetHook", err)
} else { } else {
c.Handle(500, "GetHook", err) c.Handle(500, "GetHook", err)
@@ -601,17 +599,16 @@ func SettingsGitHooksEdit(c *context.Context) {
func SettingsGitHooksEditPost(c *context.Context) { func SettingsGitHooksEditPost(c *context.Context) {
name := c.Params(":name") name := c.Params(":name")
hook, err := c.Repo.GitRepo.GetHook(name) hook, err := c.Repo.GitRepo.Hook("custom_hooks", git.HookName(name))
if err != nil { if err != nil {
if err == git.ErrNotValidHook { if err == os.ErrNotExist {
c.Handle(404, "GetHook", err) c.Handle(404, "GetHook", err)
} else { } else {
c.Handle(500, "GetHook", err) c.Handle(500, "GetHook", err)
} }
return return
} }
hook.Content = c.Query("content") if err = hook.Update(c.Query("content")); err != nil {
if err = hook.Update(); err != nil {
c.Handle(500, "hook.Update", err) c.Handle(500, "hook.Update", err)
return return
} }

View File

@@ -8,19 +8,20 @@ import (
"bytes" "bytes"
"fmt" "fmt"
gotemplate "html/template" gotemplate "html/template"
"io/ioutil"
"path" "path"
"strings" "strings"
"time"
"github.com/gogs/git-module"
"github.com/pkg/errors"
"github.com/unknwon/paginater" "github.com/unknwon/paginater"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"github.com/gogs/git-module" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/template" "gogs.io/gogs/internal/template"
"gogs.io/gogs/internal/template/highlight" "gogs.io/gogs/internal/template/highlight"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
@@ -34,32 +35,36 @@ const (
) )
func renderDirectory(c *context.Context, treeLink string) { func renderDirectory(c *context.Context, treeLink string) {
tree, err := c.Repo.Commit.SubTree(c.Repo.TreePath) tree, err := c.Repo.Commit.Subtree(c.Repo.TreePath)
if err != nil { if err != nil {
c.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err) c.NotFoundOrServerError("get subtree", gitutil.IsErrRevisionNotExist, err)
return return
} }
entries, err := tree.ListEntries() entries, err := tree.Entries()
if err != nil { if err != nil {
c.ServerError("ListEntries", err) c.ServerError("list entries", err)
return return
} }
entries.Sort() entries.Sort()
c.Data["Files"], err = entries.GetCommitsInfoWithCustomConcurrency(c.Repo.Commit, c.Repo.TreePath, conf.Repository.CommitsFetchConcurrency) c.Data["Files"], err = entries.CommitsInfo(c.Repo.Commit, git.CommitsInfoOptions{
Path: c.Repo.TreePath,
MaxConcurrency: conf.Repository.CommitsFetchConcurrency,
Timeout: 5 * time.Minute,
})
if err != nil { if err != nil {
c.ServerError("GetCommitsInfoWithCustomConcurrency", err) c.ServerError("get commits info", err)
return return
} }
var readmeFile *git.Blob var readmeFile *git.Blob
for _, entry := range entries { for _, entry := range entries {
if entry.IsDir() || !markup.IsReadmeFile(entry.Name()) { if entry.IsTree() || !markup.IsReadmeFile(entry.Name()) {
continue continue
} }
// TODO: collect all possible README files and show with priority. // TODO(unknwon): collect all possible README files and show with priority.
readmeFile = entry.Blob() readmeFile = entry.Blob()
break break
} }
@@ -69,37 +74,30 @@ func renderDirectory(c *context.Context, treeLink string) {
c.Data["ReadmeInList"] = true c.Data["ReadmeInList"] = true
c.Data["ReadmeExist"] = true c.Data["ReadmeExist"] = true
dataRc, err := readmeFile.Data() p, err := readmeFile.Bytes()
if err != nil { if err != nil {
c.ServerError("readmeFile.Data", err) c.ServerError("readmeFile.Data", err)
return return
} }
buf := make([]byte, 1024) isTextFile := tool.IsTextFile(p)
n, _ := dataRc.Read(buf)
buf = buf[:n]
isTextFile := tool.IsTextFile(buf)
c.Data["IsTextFile"] = isTextFile c.Data["IsTextFile"] = isTextFile
c.Data["FileName"] = readmeFile.Name() c.Data["FileName"] = readmeFile.Name()
if isTextFile { if isTextFile {
d, _ := ioutil.ReadAll(dataRc)
buf = append(buf, d...)
switch markup.Detect(readmeFile.Name()) { switch markup.Detect(readmeFile.Name()) {
case markup.MARKDOWN: case markup.MARKDOWN:
c.Data["IsMarkdown"] = true c.Data["IsMarkdown"] = true
buf = markup.Markdown(buf, treeLink, c.Repo.Repository.ComposeMetas()) p = markup.Markdown(p, treeLink, c.Repo.Repository.ComposeMetas())
case markup.ORG_MODE: case markup.ORG_MODE:
c.Data["IsMarkdown"] = true c.Data["IsMarkdown"] = true
buf = markup.OrgMode(buf, treeLink, c.Repo.Repository.ComposeMetas()) p = markup.OrgMode(p, treeLink, c.Repo.Repository.ComposeMetas())
case markup.IPYTHON_NOTEBOOK: case markup.IPYTHON_NOTEBOOK:
c.Data["IsIPythonNotebook"] = true c.Data["IsIPythonNotebook"] = true
c.Data["RawFileLink"] = c.Repo.RepoLink + "/raw/" + path.Join(c.Repo.BranchName, c.Repo.TreePath, readmeFile.Name()) c.Data["RawFileLink"] = c.Repo.RepoLink + "/raw/" + path.Join(c.Repo.BranchName, c.Repo.TreePath, readmeFile.Name())
default: default:
buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1) p = bytes.Replace(p, []byte("\n"), []byte(`<br>`), -1)
} }
c.Data["FileContent"] = string(buf) c.Data["FileContent"] = string(p)
} }
} }
@@ -107,9 +105,9 @@ func renderDirectory(c *context.Context, treeLink string) {
// or of directory if not in root directory. // or of directory if not in root directory.
latestCommit := c.Repo.Commit latestCommit := c.Repo.Commit
if len(c.Repo.TreePath) > 0 { if len(c.Repo.TreePath) > 0 {
latestCommit, err = c.Repo.Commit.GetCommitByPath(c.Repo.TreePath) latestCommit, err = c.Repo.Commit.CommitByPath(git.CommitByRevisionOptions{Path: c.Repo.TreePath})
if err != nil { if err != nil {
c.ServerError("GetCommitByPath", err) c.ServerError("get commit by path", err)
return return
} }
} }
@@ -126,7 +124,7 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
c.Data["IsViewFile"] = true c.Data["IsViewFile"] = true
blob := entry.Blob() blob := entry.Blob()
dataRc, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
c.Handle(500, "Data", err) c.Handle(500, "Data", err)
return return
@@ -137,11 +135,7 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
c.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name()) c.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name())
c.Data["RawFileLink"] = rawLink + "/" + c.Repo.TreePath c.Data["RawFileLink"] = rawLink + "/" + c.Repo.TreePath
buf := make([]byte, 1024) isTextFile := tool.IsTextFile(p)
n, _ := dataRc.Read(buf)
buf = buf[:n]
isTextFile := tool.IsTextFile(buf)
c.Data["IsTextFile"] = isTextFile c.Data["IsTextFile"] = isTextFile
// Assume file is not editable first. // Assume file is not editable first.
@@ -159,26 +153,23 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
c.Data["ReadmeExist"] = markup.IsReadmeFile(blob.Name()) c.Data["ReadmeExist"] = markup.IsReadmeFile(blob.Name())
d, _ := ioutil.ReadAll(dataRc)
buf = append(buf, d...)
switch markup.Detect(blob.Name()) { switch markup.Detect(blob.Name()) {
case markup.MARKDOWN: case markup.MARKDOWN:
c.Data["IsMarkdown"] = true c.Data["IsMarkdown"] = true
c.Data["FileContent"] = string(markup.Markdown(buf, path.Dir(treeLink), c.Repo.Repository.ComposeMetas())) c.Data["FileContent"] = string(markup.Markdown(p, path.Dir(treeLink), c.Repo.Repository.ComposeMetas()))
case markup.ORG_MODE: case markup.ORG_MODE:
c.Data["IsMarkdown"] = true c.Data["IsMarkdown"] = true
c.Data["FileContent"] = string(markup.OrgMode(buf, path.Dir(treeLink), c.Repo.Repository.ComposeMetas())) c.Data["FileContent"] = string(markup.OrgMode(p, path.Dir(treeLink), c.Repo.Repository.ComposeMetas()))
case markup.IPYTHON_NOTEBOOK: case markup.IPYTHON_NOTEBOOK:
c.Data["IsIPythonNotebook"] = true c.Data["IsIPythonNotebook"] = true
default: default:
// Building code view blocks with line number on server side. // Building code view blocks with line number on server side.
var fileContent string var fileContent string
if err, content := template.ToUTF8WithErr(buf); err != nil { if err, content := template.ToUTF8WithErr(p); err != nil {
if err != nil { if err != nil {
log.Error("ToUTF8WithErr: %s", err) log.Error("ToUTF8WithErr: %s", err)
} }
fileContent = string(buf) fileContent = string(p)
} else { } else {
fileContent = content fileContent = content
} }
@@ -210,11 +201,11 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
c.Data["EditFileTooltip"] = c.Tr("repo.editor.fork_before_edit") c.Data["EditFileTooltip"] = c.Tr("repo.editor.fork_before_edit")
} }
case tool.IsPDFFile(buf): case tool.IsPDFFile(p):
c.Data["IsPDFFile"] = true c.Data["IsPDFFile"] = true
case tool.IsVideoFile(buf): case tool.IsVideoFile(p):
c.Data["IsVideoFile"] = true c.Data["IsVideoFile"] = true
case tool.IsImageFile(buf): case tool.IsImageFile(p):
c.Data["IsImageFile"] = true c.Data["IsImageFile"] = true
} }
@@ -229,9 +220,9 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
} }
func setEditorconfigIfExists(c *context.Context) { func setEditorconfigIfExists(c *context.Context) {
ec, err := c.Repo.GetEditorconfig() ec, err := c.Repo.Editorconfig()
if err != nil && !git.IsErrNotExist(err) { if err != nil && !gitutil.IsErrRevisionNotExist(errors.Cause(err)) {
log.Trace("setEditorconfigIfExists.GetEditorconfig [%d]: %v", c.Repo.Repository.ID, err) log.Warn("setEditorconfigIfExists.Editorconfig [repo_id: %d]: %v", c.Repo.Repository.ID, err)
return return
} }
c.Data["Editorconfig"] = ec c.Data["Editorconfig"] = ec
@@ -277,13 +268,13 @@ func Home(c *context.Context) {
c.Data["PageIsRepoHome"] = isRootDir c.Data["PageIsRepoHome"] = isRootDir
// Get current entry user currently looking at. // Get current entry user currently looking at.
entry, err := c.Repo.Commit.GetTreeEntryByPath(c.Repo.TreePath) entry, err := c.Repo.Commit.TreeEntry(c.Repo.TreePath)
if err != nil { if err != nil {
c.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err) c.NotFoundOrServerError("get tree entry", gitutil.IsErrRevisionNotExist, err)
return return
} }
if entry.IsDir() { if entry.IsTree() {
renderDirectory(c, treeLink) renderDirectory(c, treeLink)
} else { } else {
renderFile(c, entry, treeLink, rawLink) renderFile(c, entry, treeLink, rawLink)

View File

@@ -14,11 +14,11 @@ import (
git "github.com/gogs/git-module" git "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client" api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/conf"
) )
const ( const (
@@ -517,23 +517,37 @@ func DingtalkHooksEditPost(c *context.Context, f form.NewDingtalkHook) {
} }
func TestWebhook(c *context.Context) { func TestWebhook(c *context.Context) {
var authorUsername, committerUsername string
var (
commitID string
commitMessage string
author *git.Signature
committer *git.Signature
authorUsername string
committerUsername string
nameStatus *git.NameStatus
)
// Grab latest commit or fake one if it's empty repository. // Grab latest commit or fake one if it's empty repository.
commit := c.Repo.Commit
if commit == nil { if c.Repo.Commit == nil {
commitID = git.EmptyID
commitMessage = "This is a fake commit"
ghost := db.NewGhostUser() ghost := db.NewGhostUser()
commit = &git.Commit{ author = ghost.NewGitSig()
ID: git.MustIDFromString(git.EMPTY_SHA), committer = ghost.NewGitSig()
Author: ghost.NewGitSig(),
Committer: ghost.NewGitSig(),
CommitMessage: "This is a fake commit",
}
authorUsername = ghost.Name authorUsername = ghost.Name
committerUsername = ghost.Name committerUsername = ghost.Name
nameStatus = &git.NameStatus{}
} else { } else {
commitID = c.Repo.Commit.ID.String()
commitMessage = c.Repo.Commit.Message
author = c.Repo.Commit.Author
committer = c.Repo.Commit.Committer
// Try to match email with a real user. // Try to match email with a real user.
author, err := db.GetUserByEmail(commit.Author.Email) author, err := db.GetUserByEmail(c.Repo.Commit.Author.Email)
if err == nil { if err == nil {
authorUsername = author.Name authorUsername = author.Name
} else if !errors.IsUserNotExist(err) { } else if !errors.IsUserNotExist(err) {
@@ -541,44 +555,44 @@ func TestWebhook(c *context.Context) {
return return
} }
committer, err := db.GetUserByEmail(commit.Committer.Email) user, err := db.GetUserByEmail(c.Repo.Commit.Committer.Email)
if err == nil { if err == nil {
committerUsername = committer.Name committerUsername = user.Name
} else if !errors.IsUserNotExist(err) { } else if !errors.IsUserNotExist(err) {
c.Handle(500, "GetUserByEmail.(committer)", err) c.Handle(500, "GetUserByEmail.(committer)", err)
return return
} }
}
fileStatus, err := commit.FileStatus() nameStatus, err = c.Repo.Commit.ShowNameStatus()
if err != nil { if err != nil {
c.Handle(500, "FileStatus", err) c.Handle(500, "FileStatus", err)
return return
} }
}
apiUser := c.User.APIFormat() apiUser := c.User.APIFormat()
p := &api.PushPayload{ p := &api.PushPayload{
Ref: git.BRANCH_PREFIX + c.Repo.Repository.DefaultBranch, Ref: git.RefsHeads + c.Repo.Repository.DefaultBranch,
Before: commit.ID.String(), Before: commitID,
After: commit.ID.String(), After: commitID,
Commits: []*api.PayloadCommit{ Commits: []*api.PayloadCommit{
{ {
ID: commit.ID.String(), ID: commitID,
Message: commit.Message(), Message: commitMessage,
URL: c.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(), URL: c.Repo.Repository.HTMLURL() + "/commit/" + commitID,
Author: &api.PayloadUser{ Author: &api.PayloadUser{
Name: commit.Author.Name, Name: author.Name,
Email: commit.Author.Email, Email: author.Email,
UserName: authorUsername, UserName: authorUsername,
}, },
Committer: &api.PayloadUser{ Committer: &api.PayloadUser{
Name: commit.Committer.Name, Name: committer.Name,
Email: commit.Committer.Email, Email: committer.Email,
UserName: committerUsername, UserName: committerUsername,
}, },
Added: fileStatus.Added, Added: nameStatus.Added,
Removed: fileStatus.Removed, Removed: nameStatus.Removed,
Modified: fileStatus.Modified, Modified: nameStatus.Modified,
}, },
}, },
Repo: c.Repo.Repository.APIFormat(nil), Repo: c.Repo.Repository.APIFormat(nil),

View File

@@ -5,7 +5,6 @@
package repo package repo
import ( import (
"io/ioutil"
"strings" "strings"
"time" "time"
@@ -14,6 +13,7 @@ import (
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
) )
@@ -43,27 +43,27 @@ type PageMeta struct {
} }
func renderWikiPage(c *context.Context, isViewPage bool) (*git.Repository, string) { func renderWikiPage(c *context.Context, isViewPage bool) (*git.Repository, string) {
wikiRepo, err := git.OpenRepository(c.Repo.Repository.WikiPath()) wikiRepo, err := git.Open(c.Repo.Repository.WikiPath())
if err != nil { if err != nil {
c.Handle(500, "OpenRepository", err) c.ServerError("open repository", err)
return nil, "" return nil, ""
} }
commit, err := wikiRepo.GetBranchCommit("master") commit, err := wikiRepo.BranchCommit("master")
if err != nil { if err != nil {
c.Handle(500, "GetBranchCommit", err) c.ServerError("get branch commit", err)
return nil, "" return nil, ""
} }
// Get page list. // Get page list.
if isViewPage { if isViewPage {
entries, err := commit.ListEntries() entries, err := commit.Entries()
if err != nil { if err != nil {
c.Handle(500, "ListEntries", err) c.ServerError("list entries", err)
return nil, "" return nil, ""
} }
pages := make([]PageMeta, 0, len(entries)) pages := make([]PageMeta, 0, len(entries))
for i := range entries { for i := range entries {
if entries[i].Type == git.OBJECT_BLOB && strings.HasSuffix(entries[i].Name(), ".md") { if entries[i].Type() == git.ObjectBlob && strings.HasSuffix(entries[i].Name(), ".md") {
name := strings.TrimSuffix(entries[i].Name(), ".md") name := strings.TrimSuffix(entries[i].Name(), ".md")
pages = append(pages, PageMeta{ pages = append(pages, PageMeta{
Name: name, Name: name,
@@ -86,29 +86,24 @@ func renderWikiPage(c *context.Context, isViewPage bool) (*git.Repository, strin
c.Data["title"] = pageName c.Data["title"] = pageName
c.Data["RequireHighlightJS"] = true c.Data["RequireHighlightJS"] = true
blob, err := commit.GetBlobByPath(pageName + ".md") blob, err := commit.Blob(pageName + ".md")
if err != nil { if err != nil {
if git.IsErrNotExist(err) { if gitutil.IsErrRevisionNotExist(err) {
c.Redirect(c.Repo.RepoLink + "/wiki/_pages") c.Redirect(c.Repo.RepoLink + "/wiki/_pages")
} else { } else {
c.Handle(500, "GetBlobByPath", err) c.ServerError("GetBlobByPath", err)
} }
return nil, "" return nil, ""
} }
r, err := blob.Data() p, err := blob.Bytes()
if err != nil { if err != nil {
c.Handle(500, "Data", err) c.ServerError("Data", err)
return nil, ""
}
data, err := ioutil.ReadAll(r)
if err != nil {
c.Handle(500, "ReadAll", err)
return nil, "" return nil, ""
} }
if isViewPage { if isViewPage {
c.Data["content"] = string(markup.Markdown(data, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) c.Data["content"] = string(markup.Markdown(p, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas()))
} else { } else {
c.Data["content"] = string(data) c.Data["content"] = string(p)
} }
return wikiRepo, pageName return wikiRepo, pageName
@@ -129,12 +124,12 @@ func Wiki(c *context.Context) {
} }
// Get last change information. // Get last change information.
lastCommit, err := wikiRepo.GetCommitByPath(pageName + ".md") commits, err := wikiRepo.Log(git.RefsHeads+"master", git.LogOptions{Path: pageName + ".md"})
if err != nil { if err != nil {
c.Handle(500, "GetCommitByPath", err) c.ServerError("get commits by path", err)
return return
} }
c.Data["Author"] = lastCommit.Author c.Data["Author"] = commits[0].Author
c.HTML(200, WIKI_VIEW) c.HTML(200, WIKI_VIEW)
} }
@@ -148,35 +143,35 @@ func WikiPages(c *context.Context) {
return return
} }
wikiRepo, err := git.OpenRepository(c.Repo.Repository.WikiPath()) wikiRepo, err := git.Open(c.Repo.Repository.WikiPath())
if err != nil { if err != nil {
c.Handle(500, "OpenRepository", err) c.ServerError("open repository", err)
return return
} }
commit, err := wikiRepo.GetBranchCommit("master") commit, err := wikiRepo.BranchCommit("master")
if err != nil { if err != nil {
c.Handle(500, "GetBranchCommit", err) c.ServerError("get branch commit", err)
return return
} }
entries, err := commit.ListEntries() entries, err := commit.Entries()
if err != nil { if err != nil {
c.Handle(500, "ListEntries", err) c.ServerError("list entries", err)
return return
} }
pages := make([]PageMeta, 0, len(entries)) pages := make([]PageMeta, 0, len(entries))
for i := range entries { for i := range entries {
if entries[i].Type == git.OBJECT_BLOB && strings.HasSuffix(entries[i].Name(), ".md") { if entries[i].Type() == git.ObjectBlob && strings.HasSuffix(entries[i].Name(), ".md") {
commit, err := wikiRepo.GetCommitByPath(entries[i].Name()) commits, err := wikiRepo.Log(git.RefsHeads+"master", git.LogOptions{Path: entries[i].Name()})
if err != nil { if err != nil {
c.ServerError("GetCommitByPath", err) c.ServerError("get commits by path", err)
return return
} }
name := strings.TrimSuffix(entries[i].Name(), ".md") name := strings.TrimSuffix(entries[i].Name(), ".md")
pages = append(pages, PageMeta{ pages = append(pages, PageMeta{
Name: name, Name: name,
URL: db.ToWikiPageURL(name), URL: db.ToWikiPageURL(name),
Updated: commit.Author.When, Updated: commits[0].Author.When,
}) })
} }
} }
@@ -212,7 +207,7 @@ func NewWikiPost(c *context.Context, f form.NewWiki) {
c.Data["Err_Title"] = true c.Data["Err_Title"] = true
c.RenderWithErr(c.Tr("repo.wiki.page_already_exists"), WIKI_NEW, &f) c.RenderWithErr(c.Tr("repo.wiki.page_already_exists"), WIKI_NEW, &f)
} else { } else {
c.Handle(500, "AddWikiPage", err) c.ServerError("AddWikiPage", err)
} }
return return
} }
@@ -249,7 +244,7 @@ func EditWikiPost(c *context.Context, f form.NewWiki) {
} }
if err := c.Repo.Repository.EditWikiPage(c.User, f.OldTitle, f.Title, f.Content, f.Message); err != nil { if err := c.Repo.Repository.EditWikiPage(c.User, f.OldTitle, f.Title, f.Content, f.Message); err != nil {
c.Handle(500, "EditWikiPage", err) c.ServerError("EditWikiPage", err)
return return
} }
@@ -264,7 +259,7 @@ func DeleteWikiPagePost(c *context.Context) {
pageName := db.ToWikiPageName(pageURL) pageName := db.ToWikiPageName(pageURL)
if err := c.Repo.Repository.DeleteWikiPage(c.User, pageName); err != nil { if err := c.Repo.Repository.DeleteWikiPage(c.User, pageName); err != nil {
c.Handle(500, "DeleteWikiPage", err) c.ServerError("DeleteWikiPage", err)
return return
} }

View File

@@ -5,7 +5,6 @@
package template package template
import ( import (
"container/list"
"fmt" "fmt"
"html/template" "html/template"
"mime" "mime"
@@ -21,8 +20,11 @@ import (
"golang.org/x/text/transform" "golang.org/x/text/transform"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
) )
@@ -36,6 +38,9 @@ var (
func FuncMap() []template.FuncMap { func FuncMap() []template.FuncMap {
funcMapOnce.Do(func() { funcMapOnce.Do(func() {
funcMap = []template.FuncMap{map[string]interface{}{ funcMap = []template.FuncMap{map[string]interface{}{
"BuildCommit": func() string {
return conf.BuildCommit
},
"Year": func() int { "Year": func() int {
return time.Now().Year() return time.Now().Year()
}, },
@@ -86,7 +91,6 @@ func FuncMap() []template.FuncMap {
"DateFmtShort": func(t time.Time) string { "DateFmtShort": func(t time.Time) string {
return t.Format("Jan 02, 2006") return t.Format("Jan 02, 2006")
}, },
"List": List,
"SubStr": func(str string, start, length int) string { "SubStr": func(str string, start, length int) string {
if len(str) == 0 { if len(str) == 0 {
return "" return ""
@@ -102,11 +106,10 @@ func FuncMap() []template.FuncMap {
}, },
"Join": strings.Join, "Join": strings.Join,
"EllipsisString": tool.EllipsisString, "EllipsisString": tool.EllipsisString,
"DiffTypeToStr": DiffTypeToStr, "DiffFileTypeToStr": DiffFileTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr, "DiffLineTypeToStr": DiffLineTypeToStr,
"Sha1": Sha1, "Sha1": Sha1,
"ShortSHA1": tool.ShortSHA1, "ShortSHA1": tool.ShortSHA1,
"MD5": tool.MD5,
"ActionContent2Commits": ActionContent2Commits, "ActionContent2Commits": ActionContent2Commits,
"EscapePound": EscapePound, "EscapePound": EscapePound,
"RenderCommitMessage": RenderCommitMessage, "RenderCommitMessage": RenderCommitMessage,
@@ -126,6 +129,7 @@ func FuncMap() []template.FuncMap {
} }
return "tab-size-8" return "tab-size-8"
}, },
"InferSubmoduleURL": gitutil.InferSubmoduleURL,
}} }}
}) })
return funcMap return funcMap
@@ -144,19 +148,6 @@ func NewLine2br(raw string) string {
return strings.Replace(raw, "\n", "<br>", -1) return strings.Replace(raw, "\n", "<br>", -1)
} }
func List(l *list.List) chan interface{} {
e := l.Front()
c := make(chan interface{})
go func() {
for e != nil {
c <- e.Value
e = e.Next()
}
close(c)
}()
return c
}
func Sha1(str string) string { func Sha1(str string) string {
return tool.SHA1(str) return tool.SHA1(str)
} }
@@ -269,20 +260,22 @@ func EscapePound(str string) string {
return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str)
} }
func DiffTypeToStr(diffType int) string { func DiffFileTypeToStr(typ git.DiffFileType) string {
diffTypes := map[int]string{ return map[git.DiffFileType]string{
1: "add", 2: "modify", 3: "del", 4: "rename", git.DiffFileAdd: "add",
} git.DiffFileChange: "modify",
return diffTypes[diffType] git.DiffFileDelete: "del",
git.DiffFileRename: "rename",
}[typ]
} }
func DiffLineTypeToStr(diffType int) string { func DiffLineTypeToStr(typ git.DiffLineType) string {
switch diffType { switch typ {
case 2: case git.DiffLineAdd:
return "add" return "add"
case 3: case git.DiffLineDelete:
return "del" return "del"
case 4: case git.DiffLineSection:
return "tag" return "tag"
} }
return "same" return "same"

View File

@@ -5,7 +5,7 @@
background-size: contain; background-size: contain;
} }
body:not(.full-width) { body:not(.full-width) {
font-family: "PingFang SC", "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important; font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
background-color: #fff; background-color: #fff;
overflow-y: scroll; overflow-y: scroll;
overflow-x: auto; overflow-x: auto;
@@ -23,14 +23,14 @@ h5,
.ui.menu, .ui.menu,
.ui.input input, .ui.input input,
.ui.button:not(.label) { .ui.button:not(.label) {
font-family: "PingFang SC", 'Hiragino Sans GB', "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important; font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
} }
img { img {
border-radius: 3px; border-radius: 3px;
} }
pre, pre,
code { code {
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
} }
pre.raw, pre.raw,
code.raw { code.raw {
@@ -75,7 +75,7 @@ code.wrap {
} }
.following.bar.light { .following.bar.light {
background-color: white; background-color: white;
border-bottom: 1px solid #DDDDDD; border-bottom: 1px solid #dddddd;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 3px rgba(0, 0, 0, 0.04);
} }
.following.bar .column .menu { .following.bar .column .menu {
@@ -156,7 +156,7 @@ code.wrap {
color: #d95c5c !important; color: #d95c5c !important;
} }
.ui .text.red a:hover { .ui .text.red a:hover {
color: #E67777 !important; color: #e67777 !important;
} }
.ui .text.blue { .ui .text.blue {
color: #428bca !important; color: #428bca !important;
@@ -192,7 +192,7 @@ code.wrap {
color: #6e5494 !important; color: #6e5494 !important;
} }
.ui .text.yellow { .ui .text.yellow {
color: #FBBD08 !important; color: #fbbd08 !important;
} }
.ui .text.gold { .ui .text.gold {
color: #a1882b !important; color: #a1882b !important;
@@ -235,11 +235,11 @@ code.wrap {
vertical-align: middle; vertical-align: middle;
} }
.ui .warning.header { .ui .warning.header {
background-color: #F9EDBE !important; background-color: #f9edbe !important;
border-color: #F0C36D; border-color: #f0c36d;
} }
.ui .warning.segment { .ui .warning.segment {
border-color: #F0C36D; border-color: #f0c36d;
} }
.ui .info.segment { .ui .info.segment {
border: 1px solid #c5d5dd; border: 1px solid #c5d5dd;
@@ -270,7 +270,7 @@ code.wrap {
margin-left: 25px; margin-left: 25px;
} }
.ui .sha.label { .ui .sha.label {
font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
font-size: 13px; font-size: 13px;
padding: 6px 10px 4px 10px; padding: 6px 10px 4px 10px;
font-weight: normal; font-weight: normal;
@@ -1204,7 +1204,7 @@ footer .ui.language .menu {
color: #000; color: #000;
} }
.repository .header-wrapper { .repository .header-wrapper {
background-color: #FAFAFA; background-color: #fafafa;
margin-top: -15px; margin-top: -15px;
padding-top: 15px; padding-top: 15px;
} }
@@ -1279,7 +1279,7 @@ footer .ui.language .menu {
line-height: 31px; line-height: 31px;
} }
.repository.branches:not(.settings) .ui.list > .item:not(:last-child) { .repository.branches:not(.settings) .ui.list > .item:not(:last-child) {
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
} }
.repository.branches:not(.settings) .ui.list > .item .column { .repository.branches:not(.settings) .ui.list > .item .column {
padding: 5px 15px; padding: 5px 15px;
@@ -1355,7 +1355,7 @@ footer .ui.language .menu {
padding-bottom: 8px; padding-bottom: 8px;
} }
.repository.file.list #repo-files-table tr:hover { .repository.file.list #repo-files-table tr:hover {
background-color: #ffffEE; background-color: #ffffee;
} }
.repository.file.list #file-content .header .octicon { .repository.file.list #file-content .header .octicon {
padding-right: 5px; padding-right: 5px;
@@ -1414,7 +1414,7 @@ footer .ui.language .menu {
padding: 0.1em 0.5em; padding: 0.1em 0.5em;
} }
.repository.file.list #file-content #ipython-notebook .nb-stderr { .repository.file.list #file-content #ipython-notebook .nb-stderr {
background-color: #FAA; background-color: #faa;
} }
.repository.file.list #file-content #ipython-notebook .nb-cell + .nb-cell { .repository.file.list #file-content #ipython-notebook .nb-cell + .nb-cell {
margin-top: 0.5em; margin-top: 0.5em;
@@ -1431,7 +1431,7 @@ footer .ui.language .menu {
.repository.file.list #file-content #ipython-notebook .nb-raw-cell { .repository.file.list #file-content #ipython-notebook .nb-raw-cell {
white-space: pre-wrap; white-space: pre-wrap;
background-color: #f5f2f0; background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
padding: 1em; padding: 1em;
margin: 0.5em 0; margin: 0.5em 0;
} }
@@ -1497,7 +1497,7 @@ footer .ui.language .menu {
} }
.repository.file.list #file-content .code-view * { .repository.file.list #file-content .code-view * {
font-size: 12px; font-size: 12px;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
line-height: 20px; line-height: 20px;
} }
.repository.file.list #file-content .code-view table { .repository.file.list #file-content .code-view table {
@@ -1542,6 +1542,7 @@ footer .ui.language .menu {
.repository.file.list #file-content .code-view .lines-code .hljs li { .repository.file.list #file-content .code-view .lines-code .hljs li {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
padding-left: 5px;
} }
.repository.file.list #file-content .code-view .lines-num pre li.active, .repository.file.list #file-content .code-view .lines-num pre li.active,
.repository.file.list #file-content .code-view .lines-code pre li.active, .repository.file.list #file-content .code-view .lines-code pre li.active,
@@ -1551,14 +1552,6 @@ footer .ui.language .menu {
.repository.file.list #file-content .code-view .lines-code .hljs li.active { .repository.file.list #file-content .code-view .lines-code .hljs li.active {
background: #ffffdd; background: #ffffdd;
} }
.repository.file.list #file-content .code-view .lines-num pre li:before,
.repository.file.list #file-content .code-view .lines-code pre li:before,
.repository.file.list #file-content .code-view .lines-num ol li:before,
.repository.file.list #file-content .code-view .lines-code ol li:before,
.repository.file.list #file-content .code-view .lines-num .hljs li:before,
.repository.file.list #file-content .code-view .lines-code .hljs li:before {
content: ' ';
}
.repository.file.list .sidebar { .repository.file.list .sidebar {
padding-left: 0; padding-left: 0;
} }
@@ -1606,7 +1599,7 @@ footer .ui.language .menu {
pointer-events: none; pointer-events: none;
} }
.repository.file.editor .commit-form-wrapper .commit-form:before { .repository.file.editor .commit-form-wrapper .commit-form:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }
@@ -1621,7 +1614,7 @@ footer .ui.language .menu {
.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name { .repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name {
display: inline-block; display: inline-block;
padding: 3px 6px; padding: 3px 6px;
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; font: 12px Consolas, Liberation Mono, Menlo, monospace;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
background-color: rgba(209, 227, 237, 0.45); background-color: rgba(209, 227, 237, 0.45);
border-radius: 3px; border-radius: 3px;
@@ -1668,7 +1661,7 @@ footer .ui.language .menu {
pointer-events: none; pointer-events: none;
} }
.repository.new.issue .comment.form .content:before { .repository.new.issue .comment.form .content:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }
@@ -1720,7 +1713,7 @@ footer .ui.language .menu {
margin-top: 10px; margin-top: 10px;
} }
.repository.view.issue .pull-desc code { .repository.view.issue .pull-desc code {
color: #0166E6; color: #0166e6;
} }
.repository.view.issue .pull.tabular.menu { .repository.view.issue .pull.tabular.menu {
margin-bottom: 10px; margin-bottom: 10px;
@@ -1801,7 +1794,7 @@ footer .ui.language .menu {
pointer-events: none; pointer-events: none;
} }
.repository.view.issue .comment-list .comment .content .header:before { .repository.view.issue .comment-list .comment .content .header:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }
@@ -1827,7 +1820,7 @@ footer .ui.language .menu {
} }
.repository.view.issue .comment-list .comment .content > .bottom.segment .ui.images::after { .repository.view.issue .comment-list .comment .content > .bottom.segment .ui.images::after {
clear: both; clear: both;
content: ' '; content: " ";
display: block; display: block;
} }
.repository.view.issue .comment-list .comment .content > .bottom.segment a { .repository.view.issue .comment-list .comment .content > .bottom.segment a {
@@ -1842,7 +1835,7 @@ footer .ui.language .menu {
background-color: #fff; background-color: #fff;
} }
.repository.view.issue .comment-list .comment .content > .bottom.segment a:before { .repository.view.issue .comment-list .comment .content > .bottom.segment a:before {
content: ' '; content: " ";
display: inline-block; display: inline-block;
height: 100%; height: 100%;
vertical-align: middle; vertical-align: middle;
@@ -1870,7 +1863,7 @@ footer .ui.language .menu {
} }
.repository.view.issue .comment-list .comment .ui.form textarea { .repository.view.issue .comment-list .comment .ui.form textarea {
height: 200px; height: 200px;
font-family: "Consolas", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
} }
.repository.view.issue .comment-list .comment .edit.buttons { .repository.view.issue .comment-list .comment .edit.buttons {
margin-top: 10px; margin-top: 10px;
@@ -1937,7 +1930,7 @@ footer .ui.language .menu {
pointer-events: none; pointer-events: none;
} }
.repository .comment.form .content .form:before { .repository .comment.form .content .form:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }
@@ -1956,7 +1949,7 @@ footer .ui.language .menu {
} }
.repository .comment.form .content textarea { .repository .comment.form .content textarea {
height: 200px; height: 200px;
font-family: "Consolas", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
} }
.repository .label.list { .repository .label.list {
list-style: none; list-style: none;
@@ -1965,7 +1958,7 @@ footer .ui.language .menu {
.repository .label.list > .item { .repository .label.list > .item {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #aaa;
} }
.repository .label.list > .item a { .repository .label.list > .item a {
font-size: 15px; font-size: 15px;
@@ -1989,7 +1982,7 @@ footer .ui.language .menu {
.repository .milestone.list > .item { .repository .milestone.list > .item {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #aaa;
} }
.repository .milestone.list > .item > a { .repository .milestone.list > .item > a {
padding-top: 5px; padding-top: 5px;
@@ -2054,7 +2047,7 @@ footer .ui.language .menu {
pointer-events: none; pointer-events: none;
} }
.repository.compare.pull .comment.form .content:before { .repository.compare.pull .comment.form .content:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }
@@ -2100,7 +2093,7 @@ footer .ui.language .menu {
list-style: none; list-style: none;
padding-bottom: 4px; padding-bottom: 4px;
margin-bottom: 4px; margin-bottom: 4px;
border-bottom: 1px dashed #DDD; border-bottom: 1px dashed #ddd;
padding-left: 6px; padding-left: 6px;
} }
.repository .diff-detail-box span.status { .repository .diff-detail-box span.status {
@@ -2146,7 +2139,7 @@ footer .ui.language .menu {
} }
.repository .diff-file-box .file-body.file-code .lines-num { .repository .diff-file-box .file-body.file-code .lines-num {
text-align: right; text-align: right;
color: #A7A7A7; color: #a7a7a7;
background: #fafafa; background: #fafafa;
width: 1%; width: 1%;
} }
@@ -2155,7 +2148,7 @@ footer .ui.language .menu {
text-align: center; text-align: center;
} }
.repository .diff-file-box .file-body.file-code .lines-num-old { .repository .diff-file-box .file-body.file-code .lines-num-old {
border-right: 1px solid #DDD; border-right: 1px solid #ddd;
} }
.repository .diff-file-box .code-diff { .repository .diff-file-box .code-diff {
font-size: 12px; font-size: 12px;
@@ -2175,6 +2168,7 @@ footer .ui.language .menu {
} }
.repository .diff-file-box .code-diff .lines-num::before { .repository .diff-file-box .code-diff .lines-num::before {
content: attr(data-line-number); content: attr(data-line-number);
font: Consolas, Liberation Mono, Menlo, monospace;
} }
.repository .diff-file-box .code-diff .lines-num.lines-num-old, .repository .diff-file-box .code-diff .lines-num.lines-num-old,
.repository .diff-file-box .code-diff .lines-num.lines-num-new { .repository .diff-file-box .code-diff .lines-num.lines-num-new {
@@ -2185,8 +2179,8 @@ footer .ui.language .menu {
color: #383636; color: #383636;
} }
.repository .diff-file-box .code-diff tbody tr.tag-code td { .repository .diff-file-box .code-diff tbody tr.tag-code td {
background-color: #F0F0F0 !important; background-color: #f0f0f0 !important;
border-color: #D2CECE !important; border-color: #d2cece !important;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
} }
@@ -2261,7 +2255,7 @@ footer .ui.language .menu {
font-size: 1.2em; font-size: 1.2em;
} }
.repository.release #release-list { .repository.release #release-list {
border-top: 1px solid #DDD; border-top: 1px solid #ddd;
margin-top: 20px; margin-top: 20px;
padding-top: 15px; padding-top: 15px;
} }
@@ -2286,7 +2280,7 @@ footer .ui.language .menu {
margin-top: 6px; margin-top: 6px;
} }
.repository.release #release-list > li .detail { .repository.release #release-list > li .detail {
border-left: 1px solid #DDD; border-left: 1px solid #ddd;
} }
.repository.release #release-list > li .detail .author img { .repository.release #release-list > li .detail .author img {
margin-bottom: -3px; margin-bottom: -3px;
@@ -2319,7 +2313,7 @@ footer .ui.language .menu {
left: -5px; left: -5px;
top: 40px; top: 40px;
border-radius: 6px; border-radius: 6px;
border: 1px solid #FFF; border: 1px solid #fff;
} }
.repository.new.release .target { .repository.new.release .target {
min-width: 500px; min-width: 500px;
@@ -2348,7 +2342,7 @@ footer .ui.language .menu {
.repository.forks .list .item { .repository.forks .list .item {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
} }
.repository.forks .list .item .ui.avatar { .repository.forks .list .item .ui.avatar {
float: left; float: left;
@@ -2365,7 +2359,7 @@ footer .ui.language .menu {
font-size: 48px; font-size: 48px;
} }
.repository.wiki.new .CodeMirror .CodeMirror-code { .repository.wiki.new .CodeMirror .CodeMirror-code {
font-family: "Consolas", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
} }
.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment { .repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment {
background: inherit; background: inherit;
@@ -2399,7 +2393,7 @@ footer .ui.language .menu {
line-height: 2em; line-height: 2em;
} }
.repository.settings.collaboration .collaborator.list > .item:not(:last-child) { .repository.settings.collaboration .collaborator.list > .item:not(:last-child) {
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
} }
.repository.settings.collaboration #repo-collab-form #search-user-box .results { .repository.settings.collaboration #repo-collab-form #search-user-box .results {
left: 7px; left: 7px;
@@ -2491,7 +2485,7 @@ footer .ui.language .menu {
#search-repo-box .results .item, #search-repo-box .results .item,
#search-user-box .results .item { #search-user-box .results .item {
padding: 10px 15px; padding: 10px 15px;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
cursor: pointer; cursor: pointer;
} }
#search-repo-box .results .item:hover, #search-repo-box .results .item:hover,
@@ -2510,7 +2504,7 @@ footer .ui.language .menu {
.issue.list > .item { .issue.list > .item {
padding-top: 15px; padding-top: 15px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #aaa;
} }
.issue.list > .item .title { .issue.list > .item .title {
color: #444; color: #444;
@@ -2546,7 +2540,7 @@ footer .ui.language .menu {
.ui.form .dropzone { .ui.form .dropzone {
width: 100%; width: 100%;
margin-bottom: 10px; margin-bottom: 10px;
border: 2px dashed #0087F7; border: 2px dashed #0087f7;
box-shadow: none !important; box-shadow: none !important;
} }
.ui.form .dropzone .dz-error-message { .ui.form .dropzone .dz-error-message {
@@ -2688,7 +2682,7 @@ footer .ui.language .menu {
pointer-events: none; pointer-events: none;
} }
#avatar-arrow:before { #avatar-arrow:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
@footer-margin: 40px; @footer-margin: 40px;
body:not(.full-width) { body:not(.full-width) {
font-family: "PingFang SC", "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important; font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
background-color: #fff; background-color: #fff;
overflow-y: scroll; overflow-y: scroll;
overflow-x: auto; overflow-x: auto;
@@ -10,18 +10,23 @@ body:not(.full-width) {
.ui.container:not(.fluid) { .ui.container:not(.fluid) {
width: 980px !important; width: 980px !important;
} }
h1, h2, h3, h4, h5, h1,
h2,
h3,
h4,
h5,
.ui.header, .ui.header,
.ui.menu, .ui.menu,
.ui.input input, .ui.input input,
.ui.button:not(.label) { .ui.button:not(.label) {
font-family: "PingFang SC", 'Hiragino Sans GB', "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important; font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
} }
img { img {
border-radius: 3px; border-radius: 3px;
} }
pre, code { pre,
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; code {
font-family: Consolas, Liberation Mono, Menlo, monospace;
&.raw { &.raw {
padding: 7px 12px; padding: 7px 12px;
margin: 10px 0; margin: 10px 0;
@@ -65,7 +70,7 @@ pre, code {
width: 100%; width: 100%;
&.light { &.light {
background-color: white; background-color: white;
border-bottom: 1px solid #DDDDDD; border-bottom: 1px solid #dddddd;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 3px rgba(0, 0, 0, 0.04);
} }
.column .menu { .column .menu {
@@ -84,7 +89,7 @@ pre, code {
background-color: transparent; background-color: transparent;
} }
.top.menu a.item:hover { .top.menu a.item:hover {
color: rgba(0,0,0,.45); color: rgba(0, 0, 0, 0.45);
} }
.top.menu .menu { .top.menu .menu {
z-index: 900; z-index: 900;
@@ -160,7 +165,7 @@ pre, code {
a { a {
color: #d95c5c !important; color: #d95c5c !important;
&:hover { &:hover {
color: #E67777 !important; color: #e67777 !important;
} }
} }
} }
@@ -198,7 +203,7 @@ pre, code {
color: #6e5494 !important; color: #6e5494 !important;
} }
&.yellow { &.yellow {
color: #FBBD08 !important; color: #fbbd08 !important;
} }
&.gold { &.gold {
color: #a1882b !important; color: #a1882b !important;
@@ -249,11 +254,11 @@ pre, code {
} }
.warning { .warning {
&.header { &.header {
background-color: #F9EDBE !important; background-color: #f9edbe !important;
border-color: #F0C36D; border-color: #f0c36d;
} }
&.segment { &.segment {
border-color: #F0C36D; border-color: #f0c36d;
} }
} }
.info { .info {
@@ -261,7 +266,8 @@ pre, code {
border: 1px solid #c5d5dd; border: 1px solid #c5d5dd;
&.top { &.top {
background-color: #e6f1f6 !important; background-color: #e6f1f6 !important;
h3, h4 { h3,
h4 {
margin-top: 0; margin-top: 0;
} }
h3:last-child { h3:last-child {
@@ -293,7 +299,7 @@ pre, code {
} }
.sha.label { .sha.label {
font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
font-size: 13px; font-size: 13px;
padding: 6px 10px 4px 10px; padding: 6px 10px 4px 10px;
font-weight: normal; font-weight: normal;
@@ -324,8 +330,8 @@ pre, code {
height: auto; height: auto;
border-top: none; border-top: none;
line-height: 1em; line-height: 1em;
color: rgba(0,0,0,.8); color: rgba(0, 0, 0, 0.8);
padding: .71428571em 1.14285714em !important; padding: 0.71428571em 1.14285714em !important;
font-size: 1rem; font-size: 1rem;
text-transform: none; text-transform: none;
font-weight: 400; font-weight: 400;
@@ -335,8 +341,8 @@ pre, code {
font-weight: 700; font-weight: 700;
} }
&:hover { &:hover {
background: rgba(0,0,0,.05); background: rgba(0, 0, 0, 0.05);
color: rgba(0,0,0,.8); color: rgba(0, 0, 0, 0.8);
z-index: 13; z-index: 13;
} }
} }

View File

@@ -76,7 +76,7 @@
} }
} }
.header-wrapper { .header-wrapper {
background-color: #FAFAFA; background-color: #fafafa;
margin-top: -15px; margin-top: -15px;
padding-top: 15px; padding-top: 15px;
@@ -141,7 +141,7 @@
font-size: 13px; font-size: 13px;
padding: 0 5px; padding: 0 5px;
&:first-child { &:first-child {
border-radius: .28571429rem 0 0 .28571429rem; border-radius: 0.28571429rem 0 0 0.28571429rem;
} }
} }
.icon.button { .icon.button {
@@ -160,7 +160,7 @@
margin: 0; margin: 0;
line-height: 31px; line-height: 31px;
&:not(:last-child) { &:not(:last-child) {
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
} }
.column { .column {
padding: 5px 15px; padding: 5px 15px;
@@ -240,7 +240,8 @@
&.octicon-mail-reply { &.octicon-mail-reply {
margin-right: 10px; margin-right: 10px;
} }
&.octicon-file-directory, &.octicon-file-submodule { &.octicon-file-directory,
&.octicon-file-submodule {
color: #1e70bf; color: #1e70bf;
} }
} }
@@ -250,7 +251,7 @@
padding-bottom: 8px; padding-bottom: 8px;
} }
tr:hover { tr:hover {
background-color: #ffffEE; background-color: #ffffee;
} }
} }
@@ -309,14 +310,15 @@
line-height: 1.5; line-height: 1.5;
} }
.nb-stdout, .nb-stderr { .nb-stdout,
.nb-stderr {
white-space: pre-wrap; white-space: pre-wrap;
margin: 1em 0; margin: 1em 0;
padding: 0.1em 0.5em; padding: 0.1em 0.5em;
} }
.nb-stderr { .nb-stderr {
background-color: #FAA; background-color: #faa;
} }
.nb-cell + .nb-cell { .nb-cell + .nb-cell {
@@ -338,9 +340,9 @@
.nb-raw-cell { .nb-raw-cell {
white-space: pre-wrap; white-space: pre-wrap;
background-color: #f5f2f0; background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
padding: 1em; padding: 1em;
margin: .5em 0; margin: 0.5em 0;
} }
.nb-input:before, .nb-input:before,
@@ -418,7 +420,7 @@
.code-view { .code-view {
* { * {
font-size: 12px; font-size: 12px;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
line-height: 20px; line-height: 20px;
} }
@@ -457,12 +459,10 @@
li { li {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
padding-left: 5px;
&.active { &.active {
background: #ffffdd; background: #ffffdd;
} }
&:before {
content: ' ';
}
} }
} }
} }
@@ -520,7 +520,7 @@
.branch-name { .branch-name {
display: inline-block; display: inline-block;
padding: 3px 6px; padding: 3px 6px;
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; font: 12px Consolas, Liberation Mono, Menlo, monospace;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
background-color: rgba(209, 227, 237, 0.45); background-color: rgba(209, 227, 237, 0.45);
border-radius: 3px; border-radius: 3px;
@@ -584,7 +584,6 @@
overflow-x: auto; overflow-x: auto;
} }
} }
} }
} }
&.view.issue { &.view.issue {
@@ -619,7 +618,7 @@
} }
.pull-desc { .pull-desc {
code { code {
color: #0166E6; color: #0166e6;
} }
} }
.pull { .pull {
@@ -713,7 +712,7 @@
background: #f3f4f5; background: #f3f4f5;
.ui.images::after { .ui.images::after {
clear: both; clear: both;
content: ' '; content: " ";
display: block; display: block;
} }
a { a {
@@ -727,7 +726,7 @@
max-width: 150px; max-width: 150px;
background-color: #fff; background-color: #fff;
&:before { &:before {
content:' '; content: " ";
display: inline-block; display: inline-block;
height: 100%; height: 100%;
vertical-align: middle; vertical-align: middle;
@@ -760,7 +759,7 @@
} }
textarea { textarea {
height: 200px; height: 200px;
font-family: "Consolas", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
} }
} }
@@ -840,7 +839,7 @@
} }
textarea { textarea {
height: 200px; height: 200px;
font-family: "Consolas", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
} }
} }
} }
@@ -851,7 +850,7 @@
> .item { > .item {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #aaa;
a { a {
font-size: 15px; font-size: 15px;
padding-top: 5px; padding-top: 5px;
@@ -876,7 +875,7 @@
> .item { > .item {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #aaa;
> a { > a {
padding-top: 5px; padding-top: 5px;
padding-right: 10px; padding-right: 10px;
@@ -979,7 +978,7 @@
} }
} }
&.ui.basic.striped.table tbody tr:nth-child(2n) { &.ui.basic.striped.table tbody tr:nth-child(2n) {
background-color: rgba(0, 0, 0, .02)!important; background-color: rgba(0, 0, 0, 0.02) !important;
} }
} }
@@ -995,7 +994,7 @@
list-style: none; list-style: none;
padding-bottom: 4px; padding-bottom: 4px;
margin-bottom: 4px; margin-bottom: 4px;
border-bottom: 1px dashed #DDD; border-bottom: 1px dashed #ddd;
padding-left: 6px; padding-left: 6px;
} }
} }
@@ -1048,7 +1047,7 @@
.file-body.file-code { .file-body.file-code {
.lines-num { .lines-num {
text-align: right; text-align: right;
color: #A7A7A7; color: #a7a7a7;
background: #fafafa; background: #fafafa;
width: 1%; width: 1%;
@@ -1058,7 +1057,7 @@
} }
} }
.lines-num-old { .lines-num-old {
border-right: 1px solid #DDD; border-right: 1px solid #ddd;
} }
} }
.code-diff { .code-diff {
@@ -1079,9 +1078,11 @@
&::before { &::before {
content: attr(data-line-number); content: attr(data-line-number);
font: Consolas, Liberation Mono, Menlo, monospace;
} }
&.lines-num-old, &.lines-num-new { &.lines-num-old,
&.lines-num-new {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: #383636; color: #383636;
@@ -1090,11 +1091,10 @@
} }
tbody { tbody {
tr { tr {
&.tag-code { &.tag-code {
td { td {
background-color: #F0F0F0 !important; background-color: #f0f0f0 !important;
border-color: #D2CECE!important; border-color: #d2cece !important;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
} }
@@ -1115,7 +1115,6 @@
pre { pre {
background-color: #eaffea !important; background-color: #eaffea !important;
border-color: #c1e9c1 !important; border-color: #c1e9c1 !important;
} }
} }
td { td {
@@ -1179,7 +1178,7 @@
} }
} }
.clone.button:first-child { .clone.button:first-child {
border-radius: .28571429rem 0 0 .28571429rem; border-radius: 0.28571429rem 0 0 0.28571429rem;
} }
.ui.action.small.input { .ui.action.small.input {
width: 100%; width: 100%;
@@ -1194,7 +1193,7 @@
&.release { &.release {
#release-list { #release-list {
border-top: 1px solid #DDD; border-top: 1px solid #ddd;
margin-top: 20px; margin-top: 20px;
padding-top: 15px; padding-top: 15px;
@@ -1220,7 +1219,7 @@
} }
} }
.detail { .detail {
border-left: 1px solid #DDD; border-left: 1px solid #ddd;
.author { .author {
img { img {
@@ -1260,7 +1259,7 @@
left: -5px; left: -5px;
top: 40px; top: 40px;
border-radius: 6px; border-radius: 6px;
border: 1px solid #FFF; border: 1px solid #fff;
} }
} }
} }
@@ -1299,7 +1298,7 @@
.item { .item {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
.ui.avatar { .ui.avatar {
float: left; float: left;
@@ -1327,7 +1326,7 @@
&.new { &.new {
.CodeMirror { .CodeMirror {
.CodeMirror-code { .CodeMirror-code {
font-family: "Consolas", monospace; font-family: Consolas, Liberation Mono, Menlo, monospace;
.cm-comment { .cm-comment {
background: inherit; background: inherit;
} }
@@ -1351,7 +1350,12 @@
padding-left: 25px; padding-left: 25px;
margin-left: -25px; margin-left: -25px;
h1, h2, h3, h4, h5, h6 { h1,
h2,
h3,
h4,
h5,
h6 {
&:first-of-type { &:first-of-type {
margin-top: 0; margin-top: 0;
} }
@@ -1370,7 +1374,7 @@
line-height: 2em; line-height: 2em;
&:not(:last-child) { &:not(:last-child) {
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
} }
} }
} }
@@ -1505,12 +1509,12 @@
.item { .item {
padding: 10px 15px; padding: 10px 15px;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #ddd;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background: rgba(0,0,0,.05)!important; background: rgba(0, 0, 0, 0.05) !important;
color: rgba(0,0,0,.95)!important; color: rgba(0, 0, 0, 0.95) !important;
} }
img { img {
margin-right: 8px; margin-right: 8px;
@@ -1525,7 +1529,7 @@
> .item { > .item {
padding-top: 15px; padding-top: 15px;
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #aaa;
.title { .title {
color: #444; color: #444;
font-size: 15px; font-size: 15px;
@@ -1565,7 +1569,7 @@
.dropzone { .dropzone {
width: 100%; width: 100%;
margin-bottom: 10px; margin-bottom: 10px;
border: 2px dashed #0087F7; border: 2px dashed #0087f7;
box-shadow: none !important; box-shadow: none !important;
.dz-error-message { .dz-error-message {
top: 140px; top: 140px;
@@ -1701,7 +1705,8 @@
} }
#avatar-arrow { #avatar-arrow {
&:before, &:after { &:before,
&:after {
right: 100%; right: 100%;
top: 20px; top: 20px;
border: solid transparent; border: solid transparent;
@@ -1712,7 +1717,7 @@
pointer-events: none; pointer-events: none;
} }
&:before { &:before {
border-right-color: #D4D4D5; border-right-color: #d4d4d5;
border-width: 9px; border-width: 9px;
margin-top: -9px; margin-top: -9px;
} }

View File

@@ -458,11 +458,11 @@
<dt>{{.i18n.Tr "admin.config.git.disable_diff_highlight"}}</dt> <dt>{{.i18n.Tr "admin.config.git.disable_diff_highlight"}}</dt>
<dd><i class="fa fa{{if .Git.DisableDiffHighlight}}-check{{end}}-square-o"></i></dd> <dd><i class="fa fa{{if .Git.DisableDiffHighlight}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.git.max_diff_lines"}}</dt> <dt>{{.i18n.Tr "admin.config.git.max_diff_lines"}}</dt>
<dd>{{.Git.MaxGitDiffLines}}</dd> <dd>{{.Git.MaxDiffLines}}</dd>
<dt>{{.i18n.Tr "admin.config.git.max_diff_line_characters"}}</dt> <dt>{{.i18n.Tr "admin.config.git.max_diff_line_characters"}}</dt>
<dd>{{.Git.MaxGitDiffLineCharacters}}</dd> <dd>{{.Git.MaxDiffLineChars}}</dd>
<dt>{{.i18n.Tr "admin.config.git.max_diff_files"}}</dt> <dt>{{.i18n.Tr "admin.config.git.max_diff_files"}}</dt>
<dd>{{.Git.MaxGitDiffFiles}}</dd> <dd>{{.Git.MaxDiffFiles}}</dd>
<dt>{{.i18n.Tr "admin.config.git.gc_args"}}</dt> <dt>{{.i18n.Tr "admin.config.git.gc_args"}}</dt>
<dd><code>{{.Git.GCArgs}}</code></dd> <dd><code>{{.Git.GCArgs}}</code></dd>

View File

@@ -60,7 +60,7 @@
<!-- Stylesheet --> <!-- Stylesheet -->
<link rel="stylesheet" href="{{AppSubURL}}/css/semantic-2.4.2.min.css"> <link rel="stylesheet" href="{{AppSubURL}}/css/semantic-2.4.2.min.css">
<link rel="stylesheet" href="{{AppSubURL}}/css/gogs.css?v={{MD5 AppVer}}"> <link rel="stylesheet" href="{{AppSubURL}}/css/gogs.css?v={{BuildCommit}}">
<noscript> <noscript>
<style> <style>
.dropdown:hover > .menu { display: block; } .dropdown:hover > .menu { display: block; }
@@ -70,7 +70,7 @@
<!-- JavaScript --> <!-- JavaScript -->
<script src="{{AppSubURL}}/js/semantic-2.4.2.min.js"></script> <script src="{{AppSubURL}}/js/semantic-2.4.2.min.js"></script>
<script src="{{AppSubURL}}/js/gogs.js?v={{MD5 AppVer}}"></script> <script src="{{AppSubURL}}/js/gogs.js?v={{BuildCommit}}"></script>
<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title> <title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>

View File

@@ -29,8 +29,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{ $r:= List .Commits}} {{range .Commits}}
{{range $r}}
<tr> <tr>
<td class="author"> <td class="author">
{{if .User}} {{if .User}}
@@ -47,7 +46,7 @@
{{else}} {{else}}
<a rel="nofollow" class="ui sha label" href="{{AppSubURL}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">{{ShortSHA1 .ID.String}}</a> <a rel="nofollow" class="ui sha label" href="{{AppSubURL}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">{{ShortSHA1 .ID.String}}</a>
{{end}} {{end}}
<span class="{{if gt .ParentCount 1}}grey text {{end}} has-emoji">{{RenderCommitMessage false .Summary $.RepoLink $.Repository.ComposeMetas | Str2HTML}}</span> <span class="{{if gt .ParentsCount 1}}grey text {{end}} has-emoji">{{RenderCommitMessage false .Summary $.RepoLink $.Repository.ComposeMetas | Str2HTML}}</span>
</td> </td>
<td class="grey text right aligned">{{TimeSince .Author.When $.Lang}}</td> <td class="grey text right aligned">{{TimeSince .Author.When $.Lang}}</td>
</tr> </tr>

View File

@@ -4,7 +4,7 @@
<div class="diff-detail-box diff-box"> <div class="diff-detail-box diff-box">
<div> <div>
<i class="fa fa-retweet"></i> <i class="fa fa-retweet"></i>
{{.i18n.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2HTML}} {{.i18n.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAdditions .Diff.TotalDeletions | Str2HTML}}
<div class="ui right"> <div class="ui right">
<a class="ui tiny basic toggle button" href="?style={{if .IsSplitStyle}}unified{{else}}split{{end}}">{{ if .IsSplitStyle }}{{.i18n.Tr "repo.diff.show_unified_view"}}{{else}}{{.i18n.Tr "repo.diff.show_split_view"}}{{end}}</a> <a class="ui tiny basic toggle button" href="?style={{if .IsSplitStyle}}unified{{else}}split{{end}}">{{ if .IsSplitStyle }}{{.i18n.Tr "repo.diff.show_unified_view"}}{{else}}{{.i18n.Tr "repo.diff.show_split_view"}}{{end}}</a>
<a class="ui tiny basic toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a> <a class="ui tiny basic toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
@@ -14,19 +14,19 @@
{{range .Diff.Files}} {{range .Diff.Files}}
<li> <li>
<div class="diff-counter count pull-right"> <div class="diff-counter count pull-right">
{{if not .IsBin}} {{if not .IsBinary}}
<span class="add" data-line="{{.Addition}}">{{.Addition}}</span> <span class="add" data-line="{{.NumAdditions}}">{{.NumAdditions}}</span>
<span class="bar"> <span class="bar">
<span class="pull-left add"></span> <span class="pull-left add"></span>
<span class="pull-left del"></span> <span class="pull-left del"></span>
</span> </span>
<span class="del" data-line="{{.Deletion}}">{{.Deletion}}</span> <span class="del" data-line="{{.NumDeletions}}">{{.NumDeletions}}</span>
{{else}} {{else}}
<span>{{$.i18n.Tr "repo.diff.bin"}}</span> <span>{{$.i18n.Tr "repo.diff.bin"}}</span>
{{end}} {{end}}
</div> </div>
<!-- todo finish all file status, now modify, add, delete and rename --> <!-- todo finish all file status, now modify, add, delete and rename -->
<span class="status {{DiffTypeToStr .GetType}} poping up" data-content="{{DiffTypeToStr .GetType}}" data-variation="inverted tiny" data-position="right center">&nbsp;</span> <span class="status {{DiffFileTypeToStr .Type}} poping up" data-content="{{DiffFileTypeToStr .Type}}" data-variation="inverted tiny" data-position="right center">&nbsp;</span>
<a class="file" href="#diff-{{.Index}}">{{.Name}}</a> <a class="file" href="#diff-{{.Index}}">{{.Name}}</a>
</li> </li>
{{end}} {{end}}
@@ -40,12 +40,12 @@
{{$.i18n.Tr "repo.diff.file_suppressed"}} {{$.i18n.Tr "repo.diff.file_suppressed"}}
<div class="diff-counter count ui left"> <div class="diff-counter count ui left">
{{if not $file.IsRenamed}} {{if not $file.IsRenamed}}
<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span> <span class="add" data-line="{{.NumAdditions}}">+ {{.NumAdditions}}</span>
<span class="bar"> <span class="bar">
<span class="pull-left add"></span> <span class="pull-left add"></span>
<span class="pull-left del"></span> <span class="pull-left del"></span>
</span> </span>
<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span> <span class="del" data-line="{{.NumDeletions}}">- {{.NumDeletions}}</span>
{{end}} {{end}}
</div> </div>
<span class="file">{{$file.Name}}</span> <span class="file">{{$file.Name}}</span>
@@ -55,15 +55,15 @@
<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}"> <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}">
<h4 class="ui top attached normal header"> <h4 class="ui top attached normal header">
<div class="diff-counter count ui left"> <div class="diff-counter count ui left">
{{if $file.IsBin}} {{if $file.IsBinary}}
{{$.i18n.Tr "repo.diff.bin"}} {{$.i18n.Tr "repo.diff.bin"}}
{{else if not $file.IsRenamed}} {{else if not $file.IsRenamed}}
<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span> <span class="add" data-line="{{.NumAdditions}}">+ {{.NumAdditions}}</span>
<span class="bar"> <span class="bar">
<span class="pull-left add"></span> <span class="pull-left add"></span>
<span class="pull-left del"></span> <span class="pull-left del"></span>
</span> </span>
<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span> <span class="del" data-line="{{.NumDeletions}}">- {{.NumDeletions}}</span>
{{end}} {{end}}
</div> </div>
<span class="file">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}</span> <span class="file">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}</span>
@@ -80,7 +80,7 @@
<div class="ui unstackable attached table segment"> <div class="ui unstackable attached table segment">
{{if not $file.IsRenamed}} {{if not $file.IsRenamed}}
{{$isImage := (call $.IsImageFile $file.Name)}} {{$isImage := (call $.IsImageFile $file.Name)}}
{{if and $isImage}} {{if $isImage}}
<div class="center"> <div class="center">
<img src="{{$.RawPath}}/{{EscapePound .Name}}"> <img src="{{$.RawPath}}/{{EscapePound .Name}}">
</div> </div>
@@ -92,22 +92,22 @@
{{$highlightClass := $file.HighlightClass}} {{$highlightClass := $file.HighlightClass}}
{{range $j, $section := $file.Sections}} {{range $j, $section := $file.Sections}}
{{range $k, $line := $section.Lines}} {{range $k, $line := $section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> <tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .GetType 4}} {{if eq .Type 4}}
<td class="lines-num"></td> <td class="lines-num"></td>
<td colspan="3" class="lines-code"> <td colspan="3" class="lines-code">
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre> <pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre>
</td> </td>
{{else}} {{else}}
<td class="lines-num lines-num-old" {{if $line.LeftIdx}} id="diff-{{Sha1 $file.Index}}L{{$line.LeftIdx}}" data-line-number="{{$line.LeftIdx}}"{{end}}> <td class="lines-num lines-num-old" {{if $line.LeftLine}} id="diff-{{Sha1 $file.Index}}L{{$line.LeftLine}}" data-line-number="{{$line.LeftLine}}"{{end}}>
</td> </td>
<td class="lines-code halfwidth"> <td class="lines-code halfwidth">
<pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.ComputedInlineDiffFor $line}}{{end}}</code></pre> <pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.LeftLine}}{{$section.ComputedInlineDiffFor $line}}{{end}}</code></pre>
</td> </td>
<td class="lines-num lines-num-new" {{if $line.RightIdx}} id="diff-{{Sha1 $file.Index}}R{{$line.RightIdx}}" data-line-number="{{$line.RightIdx}}"{{end}}> <td class="lines-num lines-num-new" {{if $line.RightLine}} id="diff-{{Sha1 $file.Index}}R{{$line.RightLine}}" data-line-number="{{$line.RightLine}}"{{end}}>
</td> </td>
<td class="lines-code halfwidth"> <td class="lines-code halfwidth">
<pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.RightIdx}}{{$section.ComputedInlineDiffFor $line}}{{end}}</code></pre> <pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.RightLine}}{{$section.ComputedInlineDiffFor $line}}{{end}}</code></pre>
</td> </td>
{{end}} {{end}}
</tr> </tr>

View File

@@ -2,14 +2,14 @@
{{$highlightClass := $file.HighlightClass}} {{$highlightClass := $file.HighlightClass}}
{{range $j, $section := $file.Sections}} {{range $j, $section := $file.Sections}}
{{range $k, $line := $section.Lines}} {{range $k, $line := $section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> <tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .GetType 4}} {{if eq .Type 4}}
<td colspan="2" class="lines-num"> <td colspan="2" class="lines-num">
{{/* {{if gt $j 0}}<span class="fold octicon octicon-fold"></span>{{end}} */}} {{/* {{if gt $j 0}}<span class="fold octicon octicon-fold"></span>{{end}} */}}
</td> </td>
{{else}} {{else}}
<td class="lines-num lines-num-old" {{if $line.LeftIdx}} id="diff-{{$file.Index}}L{{$line.LeftIdx}}" data-line-number="{{$line.LeftIdx}}"{{end}}></td> <td class="lines-num lines-num-old" {{if $line.LeftLine}} id="diff-{{$file.Index}}L{{$line.LeftLine}}" data-line-number="{{$line.LeftLine}}"{{end}}></td>
<td class="lines-num lines-num-new" {{if $line.RightIdx}} id="diff-{{$file.Index}}R{{$line.RightIdx}}" data-line-number="{{$line.RightIdx}}"{{end}}></td> <td class="lines-num lines-num-new" {{if $line.RightLine}} id="diff-{{$file.Index}}R{{$line.RightLine}}" data-line-number="{{$line.RightLine}}"{{end}}></td>
{{end}} {{end}}
<td class="lines-code"> <td class="lines-code">
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre> <pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre>

View File

@@ -20,7 +20,7 @@
</div> </div>
<div class="field"> <div class="field">
<label for="content">{{$.i18n.Tr "repo.settings.githook_content"}}</label> <label for="content">{{$.i18n.Tr "repo.settings.githook_content"}}</label>
<textarea id="content" name="content" wrap="off" autofocus>{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea> <textarea id="content" name="content" wrap="off" autofocus>{{.Content}}</textarea>
</div> </div>
<div class="inline field"> <div class="inline field">

View File

@@ -16,7 +16,7 @@
</div> </div>
{{range .Hooks}} {{range .Hooks}}
<div class="item"> <div class="item">
<span class="text {{if .IsActive}}green{{else}}grey{{end}}"><i class="octicon octicon-primitive-dot"></i></span> <span class="text {{if not .IsSample}}green{{else}}grey{{end}}"><i class="octicon octicon-primitive-dot"></i></span>
<span>{{.Name}}</span> <span>{{.Name}}</span>
<a class="text blue ui right" href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}"><i class="fa fa-pencil"></i></a> <a class="text blue ui right" href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}"><i class="fa fa-pencil"></i></a>
</div> </div>

View File

@@ -79,7 +79,7 @@
</div> </div>
<div class="field"> <div class="field">
<label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label>
<input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required> <input id="mirror_address" name="mirror_address" value="{{.Mirror.RawAddress}}" required>
<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>
</div> </div>

View File

@@ -23,35 +23,28 @@
<td colspan="3"><i class="octicon octicon-mail-reply"></i><a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a></td> <td colspan="3"><i class="octicon octicon-mail-reply"></i><a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a></td>
</tr> </tr>
{{end}} {{end}}
{{range $item := .Files}} {{range .Files}}
{{$entry := index $item 0}}
{{$commit := index $item 1}}
<tr> <tr>
{{if $entry.IsSubModule}} {{if .Submodule}}
<td> <td>
<span class="octicon octicon-file-submodule"></span> <span class="octicon octicon-file-submodule"></span>
{{$refURL := $commit.RefURL AppURL $.BranchLink}} <a href="{{InferSubmoduleURL .Submodule}}">{{.Entry.Name}} @ {{ShortSHA1 .Submodule.Commit}}</a>
{{if $refURL}}
<a href="{{$refURL}}">{{$entry.Name}}</a> @ <a href="{{$refURL}}/commit/{{$commit.RefID}}">{{ShortSHA1 $commit.RefID}}</a>
{{else}}
{{$entry.Name}} @ {{ShortSHA1 $commit.RefID}}
{{end}}
</td> </td>
{{else}} {{else}}
<td class="name"> <td class="name">
{{if $entry.IsLink}} {{if .Entry.IsSymlink}}
<span class="octicon octicon-file-symlink-file"></span> <span class="octicon octicon-file-symlink-file"></span>
{{else}} {{else}}
<span class="octicon octicon-file-{{if or $entry.IsDir}}directory{{else}}text{{end}}"></span> <span class="octicon octicon-file-{{if or .Entry.IsTree}}directory{{else}}text{{end}}"></span>
{{end}} {{end}}
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}">{{$entry.Name}}</a> <a href="{{EscapePound $.TreeLink}}/{{EscapePound .Entry.Name}}">{{.Entry.Name}}</a>
</td> </td>
{{end}} {{end}}
<td class="message collapsing has-emoji"> <td class="message collapsing has-emoji">
<a rel="nofollow" class="ui sha label" href="{{$.RepoLink}}/commit/{{$commit.ID}}">{{ShortSHA1 $commit.ID.String}}</a> <a rel="nofollow" class="ui sha label" href="{{$.RepoLink}}/commit/{{.Commit.ID}}">{{ShortSHA1 .Commit.ID.String}}</a>
{{RenderCommitMessage false $commit.Summary $.RepoLink $.Repository.ComposeMetas | Str2HTML}} {{RenderCommitMessage false .Commit.Summary $.RepoLink $.Repository.ComposeMetas | Str2HTML}}
</td> </td>
<td class="text grey right age">{{TimeSince $commit.Committer.When $.Lang}}</td> <td class="text grey right age">{{TimeSince .Commit.Committer.When $.Lang}}</td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>