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]
; Disables highlight of added and removed changes
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_GIT_DIFF_LINES = 1000
; Max number of characters of a line allowed in diff view
MAX_GIT_DIFF_LINE_CHARACTERS = 500
; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100
MAX_GIT_DIFF_LINE_CHARACTERS = 2000
; Arguments for command 'git gc', e.g. "--aggressive --auto"
; see more on http://git-scm.com/docs/git-gc/1.7.5
GC_ARGS =

7
go.mod
View File

@@ -5,7 +5,7 @@ go 1.12
require (
github.com/bgentry/speakeasy v0.1.0 // indirect
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/go-macaron/binding v1.0.1
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/gogs/chardet v0.0.0-20150115103509-2404f7772561
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-libravatar v0.0.0-20191106065024-33a75213d0a0
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-querystring v1.0.0 // indirect
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/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
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/macaron.v1 v1.3.4
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/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.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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
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/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 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/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
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-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-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-20180905080454-ebe1bf3edb33/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/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.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
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.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/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
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.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
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-20190102054323-c2f93a96b099/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"
"crypto/tls"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
@@ -25,7 +26,6 @@ import (
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/httplib"
"gogs.io/gogs/internal/template"
)
var (
@@ -87,7 +87,7 @@ func runHookPreReceive(c *cli.Context) error {
}
oldCommitID := string(fields[0])
newCommitID := string(fields[1])
branchName := strings.TrimPrefix(string(fields[2]), git.BRANCH_PREFIX)
branchName := git.RefShortName(string(fields[2]))
// Branch protection
repoID := com.StrTo(os.Getenv(db.ENV_REPO_ID)).MustInt64()
@@ -121,7 +121,7 @@ func runHookPreReceive(c *cli.Context) error {
}
// check and deletion
if newCommitID == git.EMPTY_SHA {
if newCommitID == git.EmptyID {
fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "")
}
@@ -221,7 +221,7 @@ func runHookPostReceive(c *cli.Context) error {
options := db.PushUpdateOptions{
OldCommitID: string(fields[0]),
NewCommitID: string(fields[1]),
RefFullName: string(fields[2]),
FullRefspec: string(fields[2]),
PusherID: com.StrTo(os.Getenv(db.ENV_AUTH_USER_ID)).MustInt64(),
PusherName: os.Getenv(db.ENV_AUTH_USER_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
reqURL := conf.Server.LocalRootURL + options.RepoUserName + "/" + options.RepoName + "/tasks/trigger?branch=" +
template.EscapePound(strings.TrimPrefix(options.RefFullName, git.BRANCH_PREFIX)) +
"&secret=" + os.Getenv(db.ENV_REPO_OWNER_SALT_MD5) +
"&pusher=" + os.Getenv(db.ENV_AUTH_USER_ID)
q := make(url.Values)
q.Add("branch", git.RefShortName(options.FullRefspec))
q.Add("secret", os.Getenv(db.ENV_REPO_OWNER_SALT_MD5))
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)
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
}).Response()
if err == nil {
resp.Body.Close()
_ = resp.Body.Close()
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 {
log.Error("Failed to trigger task: %v", err)

View File

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

View File

@@ -173,7 +173,7 @@ func (c *Context) Handle(status int, msg string, err error) {
c.Data["Title"] = "Page Not Found"
case http.StatusInternalServerError:
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) {
c.Data["ErrorMsg"] = err
}

View File

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

View File

@@ -268,9 +268,9 @@ func (pc *PushCommits) ToApiPayloadCommits(repoPath, repoURL string) ([]*api.Pay
return nil, fmt.Errorf("GetUserByEmail: %v", err)
}
fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1)
nameStatus, err := git.RepoShowNameStatus(repoPath, commit.Sha1)
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{
@@ -287,9 +287,9 @@ func (pc *PushCommits) ToApiPayloadCommits(repoPath, repoURL string) ([]*api.Pay
Email: commit.CommitterEmail,
UserName: committerUsername,
},
Added: fileStatus.Added,
Removed: fileStatus.Removed,
Modified: fileStatus.Modified,
Added: nameStatus.Added,
Removed: nameStatus.Removed,
Modified: nameStatus.Modified,
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
// in order to show custom avatar, and falls back to general avatar link.
func (push *PushCommits) AvatarLink(email string) string {
_, ok := push.avatars[email]
func (pcs *PushCommits) AvatarLink(email string) string {
_, ok := pcs.avatars[email]
if !ok {
u, err := GetUserByEmail(email)
if err != nil {
push.avatars[email] = tool.AvatarLink(email)
pcs.avatars[email] = tool.AvatarLink(email)
if !errors.IsUserNotExist(err) {
log.Error("GetUserByEmail: %v", err)
}
} 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.
@@ -474,12 +474,12 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("UpdateRepository: %v", err)
}
isNewRef := opts.OldCommitID == git.EMPTY_SHA
isDelRef := opts.NewCommitID == git.EMPTY_SHA
isNewRef := opts.OldCommitID == git.EmptyID
isDelRef := opts.NewCommitID == git.EmptyID
opType := ACTION_COMMIT_REPO
// 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
} else {
// if not the first commit, set the compare URL.
@@ -504,7 +504,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("Marshal: %v", err)
}
refName := git.RefEndName(opts.RefFullName)
refName := git.RefShortName(opts.RefFullName)
action := &Action{
ActUserID: pusher.ID,
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) {
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 {
ID int64
OwnerID int64
@@ -41,7 +41,7 @@ func updateRepositorySizes(x *xorm.Engine) (err error) {
Find(&repos); err != nil {
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 {
break
}
@@ -60,10 +60,10 @@ func updateRepositorySizes(x *xorm.Engine) (err error) {
continue
}
repoPath := filepath.Join(conf.Repository.Root, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git"
countObject, err := git.GetRepoSize(repoPath)
repoPath := strings.ToLower(filepath.Join(conf.Repository.Root, user.Name, repo.Name)) + ".git"
countObject, err := git.RepoCountObjects(repoPath)
if err != nil {
log.Warn("GetRepoSize: %v", err)
log.Warn("[migrations.v16] Count repository objects: %v", err)
continue
}

View File

@@ -5,7 +5,6 @@
package db
import (
"container/list"
"fmt"
"net/url"
"strings"
@@ -72,54 +71,17 @@ func (m *Mirror) ScheduleNextSync() {
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() {
if len(m.address) > 0 {
return
}
cfg, err := ini.Load(m.Repo.GitConfigPath())
cfg, err := ini.LoadSources(
ini.LoadOptions{IgnoreInlineComment: true},
m.Repo.GitConfigPath(),
)
if err != nil {
log.Error("Load: %v", err)
log.Error("load config: %v", err)
return
}
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
// with placeholder <credentials>.
// It returns original string if protocol is not HTTP/HTTPS.
// TODO(unknwon): Use url.Parse.
func HandleMirrorCredentials(url string, mosaics bool) string {
i := strings.Index(url, "@")
if i == -1 {
@@ -161,34 +124,21 @@ func (m *Mirror) RawAddress() string {
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.
func (m *Mirror) SaveAddress(addr string) error {
repoPath := m.Repo.RepoPath()
err := git.RemoveRemote(repoPath, "origin")
err := git.RepoRemoveRemote(repoPath, "origin")
if err != nil {
return fmt.Errorf("remove remote 'origin': %v", err)
}
err = git.AddRemote(repoPath, "origin", addr, git.AddRemoteOptions{
Mirror: true,
})
addrURL, err := url.Parse(addr)
if err != nil {
return err
}
err = git.RepoAddRemote(repoPath, "origin", addrURL.String(), git.AddRemoteOptions{MirrorFetch: true})
if err != nil {
return fmt.Errorf("add remote 'origin': %v", err)
}
@@ -196,7 +146,7 @@ func (m *Mirror) SaveAddress(addr string) error {
return nil
}
const GIT_SHORT_EMPTY_SHA = "0000000"
const gitShortEmptyID = "0000000"
// mirrorSyncResult contains information of a updated reference.
// 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
results = append(results, &mirrorSyncResult{
refName: refName,
oldCommitID: GIT_SHORT_EMPTY_SHA,
oldCommitID: gitShortEmptyID,
})
case strings.HasPrefix(lines[i], " - "): // Delete reference
results = append(results, &mirrorSyncResult{
refName: refName,
newCommitID: GIT_SHORT_EMPTY_SHA,
newCommitID: gitShortEmptyID,
})
case strings.HasPrefix(lines[i], " "): // New commits of a reference
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
// good condition to prevent long blocking on URL resolution without syncing anything.
if !git.IsRepoURLAccessible(git.NetworkOptions{
URL: m.RawAddress(),
Timeout: 10 * time.Second,
}) {
if !git.IsURLAccessible(time.Minute, m.RawAddress()) {
desc := fmt.Sprintf("Source URL of mirror repository '%s' is not accessible: %s", m.Repo.FullName(), m.MosaicsAddress())
if err := CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
@@ -393,15 +340,14 @@ func SyncMirrors() {
// - Create "Mirror Sync" webhook event
// - Create mirror sync (create, push and delete) events and trigger the "mirror sync" webhooks
var gitRepo *git.Repository
if len(results) == 0 {
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 {
@@ -411,7 +357,7 @@ func SyncMirrors() {
}
// Delete reference
if result.newCommitID == GIT_SHORT_EMPTY_SHA {
if result.newCommitID == gitShortEmptyID {
if err = MirrorSyncDeleteAction(m.Repo, result.refName); err != nil {
log.Error("MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err)
}
@@ -420,7 +366,7 @@ func SyncMirrors() {
// New reference
isNewRef := false
if result.oldCommitID == GIT_SHORT_EMPTY_SHA {
if result.oldCommitID == gitShortEmptyID {
if err = MirrorSyncCreateAction(m.Repo, result.refName); err != nil {
log.Error("MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err)
continue
@@ -429,49 +375,52 @@ func SyncMirrors() {
}
// Push commits
var commits *list.List
var commits []*git.Commit
var oldCommitID string
var newCommitID string
if !isNewRef {
oldCommitID, err = git.GetFullCommitID(gitRepo.Path, result.oldCommitID)
oldCommitID, err = gitRepo.RevParse(result.oldCommitID)
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
}
newCommitID, err = git.GetFullCommitID(gitRepo.Path, result.newCommitID)
newCommitID, err = gitRepo.RevParse(result.newCommitID)
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
}
commits, err = gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID)
commits, err = gitRepo.RevList([]string{oldCommitID + "..." + newCommitID})
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
}
} else {
refNewCommitID, err := gitRepo.GetBranchCommitID(result.refName)
} else if gitRepo.HasBranch(result.refName) {
refNewCommit, err := gitRepo.BranchCommit(result.refName)
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
}
if newCommit, err := gitRepo.GetCommit(refNewCommitID); err != nil {
log.Error("GetCommit [repo_id: %d, commit_id: %s]: %v", m.RepoID, refNewCommitID, err)
continue
} else {
// TODO: Get the commits for the new ref until the closest ancestor branch like Github does
commits, err = newCommit.CommitsBeforeLimit(10)
// TODO(unknwon): Get the commits for the new ref until the closest ancestor branch like GitHub does.
commits, err = refNewCommit.Ancestors(git.LogOptions{MaxCount: 9})
if err != nil {
log.Error("CommitsBeforeLimit [repo_id: %d, commit_id: %s]: %v", m.RepoID, refNewCommitID, err)
}
oldCommitID = git.EMPTY_SHA
newCommitID = refNewCommitID
log.Error("Failed to get ancestors [repo_id: %d, commit_id: %s]: %v", m.RepoID, refNewCommit.ID, err)
continue
}
// 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{
RefName: result.refName,
OldCommitID: oldCommitID,
NewCommitID: newCommitID,
Commits: ListToPushCommits(commits),
Commits: CommitsToPushCommits(commits),
}); err != nil {
log.Error("MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err)
continue
@@ -485,15 +434,15 @@ func SyncMirrors() {
// Get latest commit date and compare to current repository updated time,
// update if latest commit date is newer.
commitDate, err := git.GetLatestCommitDate(m.Repo.RepoPath(), "")
latestCommitTime, err := gitRepo.LatestCommitTime()
if err != nil {
log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err)
continue
} else if commitDate.Before(m.Repo.Updated) {
} else if !latestCommitTime.After(m.Repo.Updated) {
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)
continue
}

View File

@@ -7,14 +7,13 @@ package db
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
)
func Test_parseRemoteUpdateOutput(t *testing.T) {
Convey("Parse mirror remote update output", t, func() {
testCases := []struct {
tests := []struct {
output string
results []*mirrorSyncResult
expResults []*mirrorSyncResult
}{
{
`
@@ -24,85 +23,15 @@ From https://try.gogs.io/unknwon/upsteam
- [deleted] (none) -> bugfix
`,
[]*mirrorSyncResult{
{"develop", GIT_SHORT_EMPTY_SHA, ""},
{"develop", gitShortEmptyID, ""},
{"master", "b0bb24f", "1d85a4f"},
{"bugfix", "", GIT_SHORT_EMPTY_SHA},
{"bugfix", "", gitShortEmptyID},
},
},
}
for _, tc := range testCases {
results := parseRemoteUpdateOutput(tc.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])
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
assert.Equal(t, test.expResults, parseRemoteUpdateOutput(test.output))
})
}
}

View File

@@ -7,7 +7,6 @@ package db
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"
@@ -212,9 +211,9 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
}
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
headGitRepo, err := git.OpenRepository(headRepoPath)
headGitRepo, err := git.Open(headRepoPath)
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,
@@ -228,7 +227,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
var stderr string
if _, stderr, err = process.ExecTimeout(5*time.Minute,
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)
}
@@ -311,13 +310,13 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
// Push changes on base branch to upstream.
if _, stderr, err = process.ExecDir(-1, 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)
}
pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
pr.MergedCommitID, err = headGitRepo.BranchCommitID(pr.HeadBranch)
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
@@ -351,42 +350,42 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
return nil
}
l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase)
commits, err := headGitRepo.RevList([]string{pr.MergeBase + "..." + pr.MergedCommitID})
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
}
// It is possible that head branch is not fully sync with base branch for merge commits,
// so we need to get latest head commit and append merge commit manully
// to avoid strange diff commits produced.
mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch)
// NOTE: It is possible that head branch is not fully sync with base branch
// for merge commits, so we need to get latest head commit and append merge
// commit manully to avoid strange diff commits produced.
mergeCommit, err := baseGitRepo.BranchCommit(pr.BaseBranch)
if err != nil {
log.Error("GetBranchCommit: %v", err)
log.Error("Failed to get base branch %q commit: %v", pr.BaseBranch, err)
return nil
}
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 {
log.Error("ToApiPayloadCommits: %v", err)
log.Error("Failed to convert to API payload commits: %v", err)
return nil
}
p := &api.PushPayload{
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
Ref: git.RefsHeads + pr.BaseBranch,
Before: pr.MergeBase,
After: mergeCommit.ID.String(),
CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
Commits: commits,
Commits: pcs,
Repo: pr.BaseRepo.APIFormat(nil),
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
Sender: doer.APIFormat(),
}
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
@@ -599,36 +598,42 @@ func (pr *PullRequest) UpdatePatch() (err error) {
return nil
}
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
headGitRepo, err := git.Open(pr.HeadRepo.RepoPath())
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
return fmt.Errorf("open repository: %v", err)
}
// Add a temporary remote.
tmpRemote := com.ToStr(time.Now().UnixNano())
if err = headGitRepo.AddRemote(tmpRemote, RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name), true); err != nil {
return fmt.Errorf("AddRemote: %v", err)
baseRepoPath := RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name)
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() {
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
pr.MergeBase, err = headGitRepo.GetMergeBase(remoteBranch, pr.HeadBranch)
pr.MergeBase, err = headGitRepo.MergeBase(remoteBranch, pr.HeadBranch)
if err != nil {
return fmt.Errorf("GetMergeBase: %v", err)
return fmt.Errorf("get merge base: %v", err)
} 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 {
return fmt.Errorf("GetPatch: %v", err)
return fmt.Errorf("get binary patch: %v", err)
}
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
}
@@ -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)
headRepoPath := pr.HeadRepo.RepoPath()
headGitRepo, err := git.OpenRepository(headRepoPath)
headGitRepo, err := git.Open(headRepoPath)
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
return fmt.Errorf("open repository: %v", err)
}
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
return fmt.Errorf("headGitRepo.AddRemote: %v", err)
tmpRemote := fmt.Sprintf("tmp-pull-%d", pr.ID)
if err = headGitRepo.AddRemote(tmpRemote, pr.BaseRepo.RepoPath()); err != nil {
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
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.
os.Remove(path.Join(pr.BaseRepo.RepoPath(), headFile))
if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil {
return fmt.Errorf("Push: %v", err)
err = headGitRepo.Push(tmpRemote, fmt.Sprintf("%s:%s", pr.HeadBranch, headRefspec))
if err != nil {
return fmt.Errorf("push: %v", err)
}
return nil

View File

@@ -119,10 +119,10 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) {
func createTag(gitRepo *git.Repository, r *Release) error {
// Only actual create when publish.
if !r.IsDraft {
if !gitRepo.IsTagExist(r.TagName) {
commit, err := gitRepo.GetBranchCommit(r.Target)
if !gitRepo.HasTag(r.TagName) {
commit, err := gitRepo.BranchCommit(r.Target)
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.
@@ -134,15 +134,15 @@ func createTag(gitRepo *git.Repository, r *Release) error {
return err
}
} else {
commit, err := gitRepo.GetTagCommit(r.TagName)
commit, err := gitRepo.TagCommit(r.TagName)
if err != nil {
return fmt.Errorf("GetTagCommit: %v", err)
return fmt.Errorf("get tag commit: %v", err)
}
r.Sha1 = commit.ID.String()
r.NumCommits, err = commit.CommitsCount()
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, ">") {
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.
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 {
countObject, err := git.GetRepoSize(repo.RepoPath())
countObject, err := git.RepoCountObjects(repo.RepoPath())
if err != nil {
return fmt.Errorf("GetRepoSize: %v", err)
return fmt.Errorf("count repository objects: %v", err)
}
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
// about no race condition.
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.
if isWiki {
branch = ""
}
if err = git.Clone(repoPath, localPath, git.CloneRepoOptions{
Timeout: time.Duration(conf.Git.Timeout.Clone) * time.Second,
if err = git.Clone(repoPath, localPath, git.CloneOptions{
Branch: branch,
Timeout: time.Duration(conf.Git.Timeout.Clone) * time.Second,
}); err != nil {
return fmt.Errorf("git clone %s: %v", branch, err)
return fmt.Errorf("git clone [branch: %s]: %v", branch, err)
}
} else {
if err = git.Fetch(localPath, git.FetchRemoteOptions{
return nil
}
gitRepo, err := git.Open(localPath)
if err != nil {
return fmt.Errorf("open repository: %v", err)
}
if err = gitRepo.Fetch(git.FetchOptions{
Prune: true,
}); err != nil {
return fmt.Errorf("git fetch: %v", err)
return fmt.Errorf("fetch: %v", err)
}
if err = git.Checkout(localPath, git.CheckoutOptions{
Branch: branch,
}); err != nil {
return fmt.Errorf("git checkout %s: %v", branch, err)
if err = gitRepo.Checkout(branch); err != nil {
return fmt.Errorf("checkout [branch: %s]: %v", branch, err)
}
// Reset to align with remote in case of force push.
if err = git.ResetHEAD(localPath, true, "origin/"+branch); err != nil {
return fmt.Errorf("git reset --hard origin/%s: %v", branch, err)
}
rev := "origin/" + branch
if err = gitRepo.Reset(rev, git.ResetOptions{
Hard: true,
}); err != nil {
return fmt.Errorf("reset [revision: %s]: %v", rev, err)
}
return nil
}
@@ -729,9 +734,7 @@ func wikiRemoteURL(remote string) string {
remote = strings.TrimSuffix(remote, ".git")
for _, suffix := range commonWikiURLSuffixes {
wikiURL := remote + suffix
if git.IsRepoURLAccessible(git.NetworkOptions{
URL: wikiURL,
}) {
if git.IsURLAccessible(time.Minute, wikiURL) {
return wikiURL
}
}
@@ -766,23 +769,23 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
migrateTimeout := time.Duration(conf.Git.Timeout.Migrate) * time.Second
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,
Quiet: true,
Timeout: migrateTimeout,
}); err != nil {
return repo, fmt.Errorf("Clone: %v", err)
return repo, fmt.Errorf("clone: %v", err)
}
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
if len(wikiRemotePath) > 0 {
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,
Quiet: true,
Timeout: migrateTimeout,
}); 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)
}
}
@@ -799,17 +802,15 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
if !repo.IsBare {
// Try to get HEAD branch and set it as default branch.
gitRepo, err := git.OpenRepository(repoPath)
gitRepo, err := git.Open(repoPath)
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 {
return repo, fmt.Errorf("GetHEADBranch: %v", err)
}
if headBranch != nil {
repo.DefaultBranch = headBranch.Name
return repo, fmt.Errorf("get HEAD branch: %v", err)
}
repo.DefaultBranch = git.RefShortName(refspec)
if err = repo.UpdateSize(); err != nil {
log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err)
@@ -847,15 +848,15 @@ func cleanUpMigrateGitConfig(configPath string) error {
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",
"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",
}
func createDelegateHooks(repoPath string) (err error) {
for _, name := range git.HookNames {
hookPath := filepath.Join(repoPath, "hooks", name)
for _, name := range git.ServerSideHooks {
hookPath := filepath.Join(repoPath, "hooks", string(name))
if err = ioutil.WriteFile(hookPath,
[]byte(fmt.Sprintf(hooksTpls[name], conf.Repository.ScriptType, conf.AppPath(), conf.CustomConf)),
os.ModePerm); err != nil {
@@ -1005,8 +1006,8 @@ func initRepository(e Engine, repoPath string, doer *User, repo *Repository, opt
}
// Init bare new repository.
if err = git.InitRepository(repoPath, true); err != nil {
return fmt.Errorf("InitRepository: %v", err)
if err = git.Init(repoPath, git.InitOptions{Bare: true}); err != nil {
return fmt.Errorf("init repository: %v", err)
} else if err = createDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
@@ -1880,9 +1881,9 @@ func ReinitMissingRepositories() error {
for _, repo := range repos {
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
if err := git.InitRepository(repo.RepoPath(), true); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("InitRepository [%d]: %v", repo.ID, err)); err2 != nil {
return fmt.Errorf("CreateRepositoryNotice: %v", err)
if err := git.Init(repo.RepoPath(), git.InitOptions{Bare: true}); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("init repository [repo_id: %d]: %v", repo.ID, err)); err2 != nil {
return fmt.Errorf("create repository notice: %v", err)
}
}
}
@@ -1930,7 +1931,11 @@ func GitFsck() {
func(idx int, bean interface{}) error {
repo := bean.(*Repository)
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)
log.Warn(desc)
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))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
localPath := repo.LocalCopyPath()
if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil {
return fmt.Errorf("discardLocalRepoChanges: %v", err)
} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil {
return fmt.Errorf("UpdateLocalCopyBranch: %v", err)
if err = discardLocalRepoBranchChanges(localPath, oldBranch); err != nil {
return fmt.Errorf("discard changes in local copy [path: %s, branch: %s]: %v", localPath, oldBranch, err)
} else if err = repo.UpdateLocalCopyBranch(oldBranch); err != nil {
return fmt.Errorf("update branch for local copy [path: %s, branch: %s]: %v", localPath, oldBranch, err)
}
if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil {
return fmt.Errorf("CreateNewBranch: %v", err)
if err = repo.CheckoutNewBranch(oldBranch, newBranch); err != nil {
return fmt.Errorf("create new branch [base: %s, new: %s]: %v", oldBranch, newBranch, err)
}
if err = git.Push(localPath, "origin", branchName); err != nil {
return fmt.Errorf("Push: %v", err)
if err = git.RepoPush(localPath, "origin", newBranch); err != nil {
return fmt.Errorf("push [branch: %s]: %v", newBranch, err)
}
return nil

View File

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

View File

@@ -23,6 +23,7 @@ import (
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/process"
"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_ID + "=" + com.ToStr(opts.RepoID),
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
}
@@ -76,14 +77,15 @@ func discardLocalRepoBranchChanges(localPath, branch string) error {
if !com.IsExist(localPath) {
return nil
}
// No need to check if nothing in the repository.
if !git.IsBranchExist(localPath, branch) {
if !git.RepoHasBranch(localPath, branch) {
return nil
}
refName := "origin/" + branch
if err := git.ResetHEAD(localPath, true, refName); err != nil {
return fmt.Errorf("git reset --hard %s: %v", refName, err)
rev := "origin/" + branch
if err := git.RepoReset(localPath, rev, git.ResetOptions{Hard: true}); err != nil {
return fmt.Errorf("reset [revision: %s]: %v", rev, err)
}
return nil
}
@@ -92,22 +94,17 @@ func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
}
// checkoutNewBranch checks out to a new branch from the a branch name.
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
if err := git.Checkout(localPath, git.CheckoutOptions{
// CheckoutNewBranch checks out to a new branch from the a branch name.
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
if err := git.RepoCheckout(repo.LocalCopyPath(), newBranch, git.CheckoutOptions{
BaseBranch: oldBranch,
Timeout: time.Duration(conf.Git.Timeout.Pull) * time.Second,
Branch: newBranch,
OldBranch: oldBranch,
}); 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
}
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
}
type UpdateRepoFileOptions struct {
LastCommitID string
OldBranch string
@@ -135,16 +132,16 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
if opts.OldBranch != opts.NewBranch {
// 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}
}
// Otherwise, delete branch from local copy in case out of sync
if git.IsBranchExist(localPath, opts.NewBranch) {
if err = git.DeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{
if git.RepoHasBranch(localPath, opts.NewBranch) {
if err = git.RepoDeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{
Force: true,
}); 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.
// Otherwise, move the file when name changed.
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)
}
}
@@ -176,29 +173,28 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
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)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
Committer: doer.NewGitSig(),
Message: opts.Message,
}); err != nil {
} else if err = git.RepoCommit(localPath, doer.NewGitSig(), opts.Message); err != nil {
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,
OwnerName: repo.MustOwner().Name,
OwnerSalt: repo.MustOwner().Salt,
RepoID: repo.ID,
RepoName: repo.Name,
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 nil
}
// 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))
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)
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 {
return nil, fmt.Errorf("parse path: %v", err)
return nil, fmt.Errorf("parse diff: %v", err)
}
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)
}
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)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
Committer: doer.NewGitSig(),
Message: opts.Message,
}); err != nil {
} else if err = git.RepoCommit(localPath, doer.NewGitSig(), opts.Message); err != nil {
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,
OwnerName: repo.MustOwner().Name,
OwnerSalt: repo.MustOwner().Salt,
RepoID: repo.ID,
RepoName: repo.Name,
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 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)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
Committer: doer.NewGitSig(),
Message: opts.Message,
}); err != nil {
} else if err = git.RepoCommit(localPath, doer.NewGitSig(), opts.Message); err != nil {
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,
OwnerName: repo.MustOwner().Name,
OwnerSalt: repo.MustOwner().Salt,
RepoID: repo.ID,
RepoName: repo.Name,
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)
}

View File

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

View File

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

View File

@@ -86,7 +86,7 @@ func GetDingtalkPayload(p api.Payloader, event HookEventType) (payload *Dingtalk
}
func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) {
refName := git.RefEndName(p.Ref)
refName := git.RefShortName(p.Ref)
refType := strings.Title(p.RefType)
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) {
refName := git.RefEndName(p.Ref)
refName := git.RefShortName(p.Ref)
refType := strings.Title(p.RefType)
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) {
refName := git.RefEndName(p.Ref)
refName := git.RefShortName(p.Ref)
pusher := p.Pusher.FullName
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.
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)
refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
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.
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)
content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName)
return &DiscordPayload{
@@ -124,7 +124,7 @@ func getDiscordForkPayload(p *api.ForkPayload) (*DiscordPayload, error) {
func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) (*DiscordPayload, error) {
// n new commits
var (
branchName = git.RefEndName(p.Ref)
branchName = git.RefShortName(p.Ref)
commitDesc 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.
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)
refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
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.
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)
text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
return &SlackPayload{
@@ -104,7 +104,7 @@ func getSlackForkPayload(p *api.ForkPayload) (*SlackPayload, error) {
func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
// n new commits
var (
branchName = git.RefEndName(p.Ref)
branchName = git.RefShortName(p.Ref)
commitDesc string
commitString string
)

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/route/repo"
)
@@ -23,9 +24,9 @@ func GetRawFile(c *context.APIContext) {
return
}
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath)
blob, err := c.Repo.Commit.Blob(c.Repo.TreePath)
if err != nil {
c.NotFoundOrServerError("GetBlobByPath", git.IsErrNotExist, err)
c.NotFoundOrServerError("get blob", gitutil.IsErrRevisionNotExist, err)
return
}
if err = repo.ServeBlob(c.Context, blob); err != nil {
@@ -35,9 +36,9 @@ func GetRawFile(c *context.APIContext) {
func GetArchive(c *context.APIContext) {
repoPath := db.RepoPath(c.Params(":username"), c.Params(":reponame"))
gitRepo, err := git.OpenRepository(repoPath)
gitRepo, err := git.Open(repoPath)
if err != nil {
c.ServerError("OpenRepository", err)
c.ServerError("open repository", err)
return
}
c.Repo.GitRepo = gitRepo
@@ -46,16 +47,16 @@ func GetArchive(c *context.APIContext) {
}
func GetEditorconfig(c *context.APIContext) {
ec, err := c.Repo.GetEditorconfig()
ec, err := c.Repo.Editorconfig()
if err != nil {
c.NotFoundOrServerError("GetEditorconfig", git.IsErrNotExist, err)
c.NotFoundOrServerError("get .editorconfig", gitutil.IsErrRevisionNotExist, err)
return
}
fileName := c.Params("filename")
def, err := ec.GetDefinitionForFilename(fileName)
if err != nil {
c.ServerError("GetDefinitionForFilename", err)
c.ServerError("get definition for filename", err)
return
}
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
import (
@@ -6,6 +10,7 @@ import (
"github.com/gogs/git-module"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/gitutil"
)
type repoGitTree struct {
@@ -24,14 +29,14 @@ type repoGitTreeEntry struct {
}
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 {
c.NotFoundOrServerError("GetRepoGitTree", git.IsErrNotExist, err)
c.NotFoundOrServerError("get tree", gitutil.IsErrRevisionNotExist, err)
return
}
entries, err := gitTree.ListEntries()
entries, err := gitTree.Entries()
if err != nil {
c.ServerError("GetRepoGitTree", err)
c.ServerError("list entries", err)
return
}
@@ -48,7 +53,7 @@ func GetRepoGitTree(c *context.APIContext) {
children := make([]*repoGitTreeEntry, 0, len(entries))
for _, entry := range entries {
var mode string
switch entry.Type {
switch entry.Type() {
case git.ObjectCommit:
mode = "160000"
case git.ObjectTree:
@@ -63,10 +68,10 @@ func GetRepoGitTree(c *context.APIContext) {
children = append(children, &repoGitTreeEntry{
Path: entry.Name(),
Mode: mode,
Type: string(entry.Type),
Type: string(entry.Type()),
Size: entry.Size(),
Sha: entry.ID.String(),
URL: fmt.Sprintf(templateURL+"/%s", entry.ID.String()),
Sha: entry.ID().String(),
URL: fmt.Sprintf(templateURL+"/%s", entry.ID().String()),
})
}
c.JSONSuccess(&repoGitTree{

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,8 +6,6 @@ package repo
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
@@ -303,30 +301,23 @@ func RetrieveRepoMetas(c *context.Context, repo *db.Repository) []*db.Label {
}
func getFileContentFromDefaultBranch(c *context.Context, filename string) (string, bool) {
var r io.Reader
var bytes []byte
if c.Repo.Commit == nil {
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 {
return "", false
}
}
entry, err := c.Repo.Commit.GetTreeEntryByPath(filename)
entry, err := c.Repo.Commit.TreeEntry(filename)
if err != nil {
return "", false
}
r, err = entry.Blob().Data()
p, err := entry.Blob().Bytes()
if err != nil {
return "", false
}
bytes, err = ioutil.ReadAll(r)
if err != nil {
return "", false
}
return string(bytes), true
return string(p), true
}
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.Repo.IsWriter() && c.Repo.GitRepo.IsBranchExist(pull.HeadBranch) &&
c.Repo.IsWriter() && c.Repo.GitRepo.HasBranch(pull.HeadBranch) &&
!branchProtected
c.Data["DeleteBranchLink"] = c.Repo.MakeURL(url.URL{

View File

@@ -5,7 +5,6 @@
package repo
import (
"container/list"
"path"
"strings"
@@ -19,6 +18,7 @@ import (
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/gitutil"
"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
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 {
c.ServerError("Repo.GitRepo.CommitsCountBetween", err)
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 {
c.ServerError("Repo.GitRepo.FilesCountBetween", err)
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
pull := issue.PullRequest
@@ -208,14 +210,14 @@ func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestIn
)
if pull.HeadRepo != nil {
headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath())
headGitRepo, err = git.Open(pull.HeadRepo.RepoPath())
if err != nil {
c.ServerError("OpenRepository", err)
c.ServerError("open repository", err)
return nil
}
}
if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBranch) {
if pull.HeadRepo == nil || !headGitRepo.HasBranch(pull.HeadBranch) {
c.Data["IsPullReuqestBroken"] = true
c.Data["HeadTarget"] = "deleted"
c.Data["NumCommits"] = 0
@@ -223,8 +225,8 @@ func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestIn
return nil
}
prInfo, err := headGitRepo.GetPullRequestInfo(db.RepoPath(repo.Owner.Name, repo.Name),
pull.BaseBranch, pull.HeadBranch)
baseRepoPath := db.RepoPath(repo.Owner.Name, repo.Name)
prMeta, err := gitutil.Module.PullRequestMeta(headGitRepo.Path(), baseRepoPath, pull.HeadBranch, pull.BaseBranch)
if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") {
c.Data["IsPullReuqestBroken"] = true
@@ -237,9 +239,9 @@ func PrepareViewPullInfo(c *context.Context, issue *db.Issue) *git.PullRequestIn
c.ServerError("GetPullRequestInfo", err)
return nil
}
c.Data["NumCommits"] = prInfo.Commits.Len()
c.Data["NumFiles"] = prInfo.NumFiles
return prInfo
c.Data["NumCommits"] = len(prMeta.Commits)
c.Data["NumFiles"] = prMeta.NumFiles
return prMeta
}
func ViewPullCommits(c *context.Context) {
@@ -257,25 +259,25 @@ func ViewPullCommits(c *context.Context) {
c.Data["Reponame"] = pull.HeadRepo.Name
}
var commits *list.List
var commits []*git.Commit
if pull.HasMerged {
PrepareMergedViewPullInfo(c, issue)
if c.Written() {
return
}
startCommit, err := c.Repo.GitRepo.GetCommit(pull.MergeBase)
startCommit, err := c.Repo.GitRepo.CatFileCommit(pull.MergeBase)
if err != nil {
c.ServerError("Repo.GitRepo.GetCommit", err)
c.ServerError("get commit of merge base", err)
return
}
endCommit, err := c.Repo.GitRepo.GetCommit(pull.MergedCommitID)
endCommit, err := c.Repo.GitRepo.CatFileCommit(pull.MergedCommitID)
if err != nil {
c.ServerError("Repo.GitRepo.GetCommit", err)
c.ServerError("get merged commit", err)
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 {
c.ServerError("Repo.GitRepo.CommitsBetween", err)
c.ServerError("list commits", err)
return
}
@@ -290,9 +292,8 @@ func ViewPullCommits(c *context.Context) {
commits = prInfo.Commits
}
commits = db.ValidateCommitsWithEmails(commits)
c.Data["Commits"] = commits
c.Data["CommitsCount"] = commits.Len()
c.Data["Commits"] = db.ValidateCommitsWithEmails(commits)
c.Data["CommitsCount"] = len(commits)
c.Success(PULL_COMMITS)
}
@@ -308,7 +309,7 @@ func ViewPullFiles(c *context.Context) {
pull := issue.PullRequest
var (
diffRepoPath string
diffGitRepo *git.Repository
startCommitID string
endCommitID string
gitRepo *git.Repository
@@ -320,7 +321,7 @@ func ViewPullFiles(c *context.Context) {
return
}
diffRepoPath = c.Repo.GitRepo.Path
diffGitRepo = c.Repo.GitRepo
startCommitID = pull.MergeBase
endCommitID = pull.MergedCommitID
gitRepo = c.Repo.GitRepo
@@ -335,37 +336,38 @@ func ViewPullFiles(c *context.Context) {
headRepoPath := db.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
headGitRepo, err := git.OpenRepository(headRepoPath)
headGitRepo, err := git.Open(headRepoPath)
if err != nil {
c.ServerError("OpenRepository", err)
c.ServerError("open repository", err)
return
}
headCommitID, err := headGitRepo.GetBranchCommitID(pull.HeadBranch)
headCommitID, err := headGitRepo.BranchCommitID(pull.HeadBranch)
if err != nil {
c.ServerError("GetBranchCommitID", err)
c.ServerError("get head branch commit ID", err)
return
}
diffRepoPath = headRepoPath
diffGitRepo = headGitRepo
startCommitID = prInfo.MergeBase
endCommitID = headCommitID
gitRepo = headGitRepo
}
diff, err := db.GetDiffRange(diffRepoPath,
startCommitID, endCommitID, conf.Git.MaxGitDiffLines,
conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles)
diff, err := gitutil.RepoDiff(diffGitRepo,
endCommitID, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars,
git.DiffOptions{Base: startCommitID},
)
if err != nil {
c.ServerError("GetDiffRange", err)
c.ServerError("get diff", err)
return
}
c.Data["Diff"] = diff
c.Data["DiffNotAvailable"] = diff.NumFiles() == 0
commit, err := gitRepo.GetCommit(endCommitID)
commit, err := gitRepo.CatFileCommit(endCommitID)
if err != nil {
c.ServerError("GetCommit", err)
c.ServerError("get commit", err)
return
}
@@ -424,7 +426,7 @@ func MergePullRequest(c *context.Context) {
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
// Get compared branches information
@@ -473,7 +475,7 @@ func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Reposi
c.Repo.PullRequest.SameRepo = isSameRepo
// Check if base branch is valid.
if !c.Repo.GitRepo.IsBranchExist(baseBranch) {
if !c.Repo.GitRepo.HasBranch(baseBranch) {
c.NotFound()
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, "", ""
}
headGitRepo, err = git.OpenRepository(db.RepoPath(headUser.Name, headRepo.Name))
headGitRepo, err = git.Open(db.RepoPath(headUser.Name, headRepo.Name))
if err != nil {
c.ServerError("OpenRepository", err)
c.ServerError("open repository", err)
return nil, nil, nil, nil, "", ""
}
} else {
@@ -514,31 +516,32 @@ func ParseCompareInfo(c *context.Context) (*db.User, *db.Repository, *git.Reposi
}
// Check if head branch is valid.
if !headGitRepo.IsBranchExist(headBranch) {
if !headGitRepo.HasBranch(headBranch) {
c.NotFound()
return nil, nil, nil, nil, "", ""
}
headBranches, err := headGitRepo.GetBranches()
headBranches, err := headGitRepo.Branches()
if err != nil {
c.ServerError("GetBranches", err)
c.ServerError("get branches", err)
return nil, nil, nil, nil, "", ""
}
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 git.IsErrNoMergeBase(err) {
if gitutil.IsErrNoMergeBase(err) {
c.Data["IsNoMergeBase"] = true
c.Success(COMPARE_PULL)
} else {
c.ServerError("GetPullRequestInfo", err)
c.ServerError("get pull request meta", err)
}
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(
@@ -546,8 +549,9 @@ func PrepareCompareDiff(
headUser *db.User,
headRepo *db.Repository,
headGitRepo *git.Repository,
prInfo *git.PullRequestInfo,
baseBranch, headBranch string) bool {
meta *gitutil.PullRequestMeta,
headBranch string,
) bool {
var (
repo = c.Repo.Repository
@@ -557,44 +561,44 @@ func PrepareCompareDiff(
// Get diff information.
c.Data["CommitRepoLink"] = headRepo.Link()
headCommitID, err := headGitRepo.GetBranchCommitID(headBranch)
headCommitID, err := headGitRepo.BranchCommitID(headBranch)
if err != nil {
c.ServerError("GetBranchCommitID", err)
c.ServerError("get head branch commit ID", err)
return false
}
c.Data["AfterCommitID"] = headCommitID
if headCommitID == prInfo.MergeBase {
if headCommitID == meta.MergeBase {
c.Data["IsNothingToCompare"] = true
return true
}
diff, err := db.GetDiffRange(db.RepoPath(headUser.Name, headRepo.Name),
prInfo.MergeBase, headCommitID, conf.Git.MaxGitDiffLines,
conf.Git.MaxGitDiffLineCharacters, conf.Git.MaxGitDiffFiles)
diff, err := gitutil.RepoDiff(headGitRepo,
headCommitID, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars,
git.DiffOptions{Base: meta.MergeBase},
)
if err != nil {
c.ServerError("GetDiffRange", err)
c.ServerError("get repository diff", err)
return false
}
c.Data["Diff"] = diff
c.Data["DiffNotAvailable"] = diff.NumFiles() == 0
headCommit, err := headGitRepo.GetCommit(headCommitID)
headCommit, err := headGitRepo.CatFileCommit(headCommitID)
if err != nil {
c.ServerError("GetCommit", err)
c.ServerError("get head commit", err)
return false
}
prInfo.Commits = db.ValidateCommitsWithEmails(prInfo.Commits)
c.Data["Commits"] = prInfo.Commits
c.Data["CommitCount"] = prInfo.Commits.Len()
c.Data["Commits"] = db.ValidateCommitsWithEmails(meta.Commits)
c.Data["CommitCount"] = len(meta.Commits)
c.Data["Username"] = headUser.Name
c.Data["Reponame"] = headRepo.Name
c.Data["IsImageFile"] = headCommit.IsImageFile
headTarget := path.Join(headUser.Name, repo.Name)
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)
return false
}
@@ -625,7 +629,7 @@ func CompareAndPullRequest(c *context.Context) {
return
}
nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch)
nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, headBranch)
if c.Written() {
return
}
@@ -667,7 +671,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
attachments []string
)
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(c)
headUser, headRepo, headGitRepo, meta, baseBranch, headBranch := ParseCompareInfo(c)
if c.Written() {
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
// something to compare or not.
PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch)
PrepareCompareDiff(c, headUser, headRepo, headGitRepo, meta, headBranch)
if c.Written() {
return
}
@@ -695,9 +699,9 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
return
}
patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch)
patch, err := headGitRepo.DiffBinary(meta.MergeBase, headBranch)
if err != nil {
c.ServerError("GetPatch", err)
c.ServerError("get patch", err)
return
}
@@ -720,7 +724,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
BaseBranch: baseBranch,
HeadRepo: headRepo,
BaseRepo: repo,
MergeBase: prInfo.MergeBase,
MergeBase: meta.MergeBase,
Type: db.PULL_REQUEST_GOGS,
}
// 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"
"strings"
"github.com/gogs/git-module"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/conf"
)
const (
@@ -26,14 +28,14 @@ const (
func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *db.Release, countCache map[string]int64) error {
// Get count if not exists
if _, ok := countCache[release.Target]; !ok {
if repoCtx.GitRepo.IsBranchExist(release.Target) {
commit, err := repoCtx.GitRepo.GetBranchCommit(release.Target)
if repoCtx.GitRepo.HasBranch(release.Target) {
commit, err := repoCtx.GitRepo.BranchCommit(release.Target)
if err != nil {
return fmt.Errorf("GetBranchCommit: %v", err)
return fmt.Errorf("get branch commit: %v", err)
}
countCache[release.Target], err = commit.CommitsCount()
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)
return fmt.Errorf("count commits: %v", err)
}
} else {
// Use NumCommits of the newest release on that target
@@ -49,13 +51,13 @@ func Releases(c *context.Context) {
c.Data["PageIsViewFiles"] = 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 {
c.Handle(500, fmt.Sprintf("GetTags '%s'", c.Repo.Repository.RepoPath()), err)
c.ServerError("get tags", err)
return
}
releases, err := db.GetPublishedReleasesByRepoID(c.Repo.Repository.ID, tagsResult.Tags...)
releases, err := db.GetPublishedReleasesByRepoID(c.Repo.Repository.ID, tagsPage.Tags...)
if err != nil {
c.Handle(500, "GetPublishedReleasesByRepoID", err)
return
@@ -64,8 +66,8 @@ func Releases(c *context.Context) {
// Temproray cache commits count of used branches to speed up.
countCache := make(map[string]int64)
results := make([]*db.Release, len(tagsResult.Tags))
for i, rawTag := range tagsResult.Tags {
results := make([]*db.Release, len(tagsPage.Tags))
for i, rawTag := range tagsPage.Tags {
for j, r := range releases {
if r == nil || r.TagName != rawTag {
continue
@@ -89,9 +91,9 @@ func Releases(c *context.Context) {
// No published release matches this tag
if results[i] == nil {
commit, err := c.Repo.GitRepo.GetTagCommit(rawTag)
commit, err := c.Repo.GitRepo.TagCommit(rawTag)
if err != nil {
c.Handle(500, "GetTagCommit", err)
c.Handle(500, "get tag commit", err)
return
}
@@ -103,7 +105,7 @@ func Releases(c *context.Context) {
results[i].NumCommits, err = commit.CommitsCount()
if err != nil {
c.Handle(500, "CommitsCount", err)
c.ServerError("count commits", err)
return
}
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
var drafts []*db.Release
if tagsResult.HasLatest {
if tagsPage.HasLatest {
drafts, err = db.GetDraftReleasesByRepoID(c.Repo.Repository.ID)
if err != nil {
c.Handle(500, "GetDraftReleasesByRepoID", err)
@@ -140,9 +142,9 @@ func Releases(c *context.Context) {
}
c.Data["Releases"] = results
c.Data["HasPrevious"] = !tagsResult.HasLatest
c.Data["ReachEnd"] = tagsResult.ReachEnd
c.Data["PreviousAfter"] = tagsResult.PreviousAfter
c.Data["HasPrevious"] = !tagsPage.HasLatest
c.Data["ReachEnd"] = !tagsPage.HasNext
c.Data["PreviousAfter"] = tagsPage.PreviousAfter
if len(results) > 0 {
c.Data["NextAfter"] = results[len(results)-1].TagName
}
@@ -175,14 +177,14 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
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)
return
}
// Use current time if tag not yet exist, otherwise get time from Git
var tagCreatedUnix int64
tag, err := c.Repo.GitRepo.GetTag(f.TagName)
tag, err := c.Repo.GitRepo.Tag(git.RefsTags + f.TagName)
if err == nil {
commit, err := tag.Commit()
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 {
c.Handle(500, "GetBranchCommit", err)
c.ServerError("get branch commit", err)
return
}
commitsCount, err := commit.CommitsCount()
if err != nil {
c.Handle(500, "CommitsCount", err)
c.ServerError("count commits", err)
return
}

View File

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

View File

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

View File

@@ -8,19 +8,20 @@ import (
"bytes"
"fmt"
gotemplate "html/template"
"io/ioutil"
"path"
"strings"
"time"
"github.com/gogs/git-module"
"github.com/pkg/errors"
"github.com/unknwon/paginater"
log "unknwon.dev/clog/v2"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/template"
"gogs.io/gogs/internal/template/highlight"
"gogs.io/gogs/internal/tool"
@@ -34,32 +35,36 @@ const (
)
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 {
c.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err)
c.NotFoundOrServerError("get subtree", gitutil.IsErrRevisionNotExist, err)
return
}
entries, err := tree.ListEntries()
entries, err := tree.Entries()
if err != nil {
c.ServerError("ListEntries", err)
c.ServerError("list entries", err)
return
}
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 {
c.ServerError("GetCommitsInfoWithCustomConcurrency", err)
c.ServerError("get commits info", err)
return
}
var readmeFile *git.Blob
for _, entry := range entries {
if entry.IsDir() || !markup.IsReadmeFile(entry.Name()) {
if entry.IsTree() || !markup.IsReadmeFile(entry.Name()) {
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()
break
}
@@ -69,37 +74,30 @@ func renderDirectory(c *context.Context, treeLink string) {
c.Data["ReadmeInList"] = true
c.Data["ReadmeExist"] = true
dataRc, err := readmeFile.Data()
p, err := readmeFile.Bytes()
if err != nil {
c.ServerError("readmeFile.Data", err)
return
}
buf := make([]byte, 1024)
n, _ := dataRc.Read(buf)
buf = buf[:n]
isTextFile := tool.IsTextFile(buf)
isTextFile := tool.IsTextFile(p)
c.Data["IsTextFile"] = isTextFile
c.Data["FileName"] = readmeFile.Name()
if isTextFile {
d, _ := ioutil.ReadAll(dataRc)
buf = append(buf, d...)
switch markup.Detect(readmeFile.Name()) {
case markup.MARKDOWN:
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:
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:
c.Data["IsIPythonNotebook"] = true
c.Data["RawFileLink"] = c.Repo.RepoLink + "/raw/" + path.Join(c.Repo.BranchName, c.Repo.TreePath, readmeFile.Name())
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.
latestCommit := c.Repo.Commit
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 {
c.ServerError("GetCommitByPath", err)
c.ServerError("get commit by path", err)
return
}
}
@@ -126,7 +124,7 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
c.Data["IsViewFile"] = true
blob := entry.Blob()
dataRc, err := blob.Data()
p, err := blob.Bytes()
if err != nil {
c.Handle(500, "Data", err)
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["RawFileLink"] = rawLink + "/" + c.Repo.TreePath
buf := make([]byte, 1024)
n, _ := dataRc.Read(buf)
buf = buf[:n]
isTextFile := tool.IsTextFile(buf)
isTextFile := tool.IsTextFile(p)
c.Data["IsTextFile"] = isTextFile
// 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())
d, _ := ioutil.ReadAll(dataRc)
buf = append(buf, d...)
switch markup.Detect(blob.Name()) {
case markup.MARKDOWN:
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:
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:
c.Data["IsIPythonNotebook"] = true
default:
// Building code view blocks with line number on server side.
var fileContent string
if err, content := template.ToUTF8WithErr(buf); err != nil {
if err, content := template.ToUTF8WithErr(p); err != nil {
if err != nil {
log.Error("ToUTF8WithErr: %s", err)
}
fileContent = string(buf)
fileContent = string(p)
} else {
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")
}
case tool.IsPDFFile(buf):
case tool.IsPDFFile(p):
c.Data["IsPDFFile"] = true
case tool.IsVideoFile(buf):
case tool.IsVideoFile(p):
c.Data["IsVideoFile"] = true
case tool.IsImageFile(buf):
case tool.IsImageFile(p):
c.Data["IsImageFile"] = true
}
@@ -229,9 +220,9 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
}
func setEditorconfigIfExists(c *context.Context) {
ec, err := c.Repo.GetEditorconfig()
if err != nil && !git.IsErrNotExist(err) {
log.Trace("setEditorconfigIfExists.GetEditorconfig [%d]: %v", c.Repo.Repository.ID, err)
ec, err := c.Repo.Editorconfig()
if err != nil && !gitutil.IsErrRevisionNotExist(errors.Cause(err)) {
log.Warn("setEditorconfigIfExists.Editorconfig [repo_id: %d]: %v", c.Repo.Repository.ID, err)
return
}
c.Data["Editorconfig"] = ec
@@ -277,13 +268,13 @@ func Home(c *context.Context) {
c.Data["PageIsRepoHome"] = isRootDir
// 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 {
c.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err)
c.NotFoundOrServerError("get tree entry", gitutil.IsErrRevisionNotExist, err)
return
}
if entry.IsDir() {
if entry.IsTree() {
renderDirectory(c, treeLink)
} else {
renderFile(c, entry, treeLink, rawLink)

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
@footer-margin: 40px;
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;
overflow-y: scroll;
overflow-x: auto;
@@ -10,18 +10,23 @@ body:not(.full-width) {
.ui.container:not(.fluid) {
width: 980px !important;
}
h1, h2, h3, h4, h5,
h1,
h2,
h3,
h4,
h5,
.ui.header,
.ui.menu,
.ui.input input,
.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 {
border-radius: 3px;
}
pre, code {
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
pre,
code {
font-family: Consolas, Liberation Mono, Menlo, monospace;
&.raw {
padding: 7px 12px;
margin: 10px 0;
@@ -65,7 +70,7 @@ pre, code {
width: 100%;
&.light {
background-color: white;
border-bottom: 1px solid #DDDDDD;
border-bottom: 1px solid #dddddd;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.04);
}
.column .menu {
@@ -84,7 +89,7 @@ pre, code {
background-color: transparent;
}
.top.menu a.item:hover {
color: rgba(0,0,0,.45);
color: rgba(0, 0, 0, 0.45);
}
.top.menu .menu {
z-index: 900;
@@ -160,7 +165,7 @@ pre, code {
a {
color: #d95c5c !important;
&:hover {
color: #E67777 !important;
color: #e67777 !important;
}
}
}
@@ -198,7 +203,7 @@ pre, code {
color: #6e5494 !important;
}
&.yellow {
color: #FBBD08 !important;
color: #fbbd08 !important;
}
&.gold {
color: #a1882b !important;
@@ -249,11 +254,11 @@ pre, code {
}
.warning {
&.header {
background-color: #F9EDBE !important;
border-color: #F0C36D;
background-color: #f9edbe !important;
border-color: #f0c36d;
}
&.segment {
border-color: #F0C36D;
border-color: #f0c36d;
}
}
.info {
@@ -261,7 +266,8 @@ pre, code {
border: 1px solid #c5d5dd;
&.top {
background-color: #e6f1f6 !important;
h3, h4 {
h3,
h4 {
margin-top: 0;
}
h3:last-child {
@@ -293,7 +299,7 @@ pre, code {
}
.sha.label {
font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace;
font-family: Consolas, Liberation Mono, Menlo, monospace;
font-size: 13px;
padding: 6px 10px 4px 10px;
font-weight: normal;
@@ -324,8 +330,8 @@ pre, code {
height: auto;
border-top: none;
line-height: 1em;
color: rgba(0,0,0,.8);
padding: .71428571em 1.14285714em !important;
color: rgba(0, 0, 0, 0.8);
padding: 0.71428571em 1.14285714em !important;
font-size: 1rem;
text-transform: none;
font-weight: 400;
@@ -335,8 +341,8 @@ pre, code {
font-weight: 700;
}
&:hover {
background: rgba(0,0,0,.05);
color: rgba(0,0,0,.8);
background: rgba(0, 0, 0, 0.05);
color: rgba(0, 0, 0, 0.8);
z-index: 13;
}
}
@@ -364,7 +370,7 @@ footer {
text-align: center;
color: #428bca;
}
.links >* {
.links > * {
border-left: 1px solid #d6d6d6;
padding-left: 8px;
margin-left: 5px;
@@ -440,10 +446,10 @@ footer {
}
// Reset CSS to prevent UI breaks
.ui.dropdown .menu>.item>.image,
.ui.dropdown .menu>.item>img,
.ui.dropdown>.text>.image,
.ui.dropdown>.text>img {
.ui.dropdown .menu > .item > .image,
.ui.dropdown .menu > .item > img,
.ui.dropdown > .text > .image,
.ui.dropdown > .text > img {
vertical-align: middle;
margin-top: 0;
margin-bottom: 0;

View File

@@ -55,7 +55,7 @@
}
.ui.list {
.hide {
display: none!important;
display: none !important;
}
.item {
padding: 0px;
@@ -76,7 +76,7 @@
}
}
.header-wrapper {
background-color: #FAFAFA;
background-color: #fafafa;
margin-top: -15px;
padding-top: 15px;
@@ -102,8 +102,8 @@
.menu {
max-height: 300px;
overflow-x: auto;
right: 0!important;
left: auto!important;
right: 0 !important;
left: auto !important;
}
.dropdown.item {
margin: 1px;
@@ -141,26 +141,26 @@
font-size: 13px;
padding: 0 5px;
&:first-child {
border-radius: .28571429rem 0 0 .28571429rem;
border-radius: 0.28571429rem 0 0 0.28571429rem;
}
}
.icon.button {
padding: 0 10px;
}
.dropdown .menu {
right: 0!important;
left: auto!important;
right: 0 !important;
left: auto !important;
}
}
&.branches:not(.settings) {
.ui.list {
padding: 0;
>.item {
> .item {
margin: 0;
line-height: 31px;
&:not(:last-child) {
border-bottom: 1px solid #DDD;
border-bottom: 1px solid #ddd;
}
.column {
padding: 5px 15px;
@@ -240,7 +240,8 @@
&.octicon-mail-reply {
margin-right: 10px;
}
&.octicon-file-directory, &.octicon-file-submodule {
&.octicon-file-directory,
&.octicon-file-submodule {
color: #1e70bf;
}
}
@@ -250,7 +251,7 @@
padding-bottom: 8px;
}
tr:hover {
background-color: #ffffEE;
background-color: #ffffee;
}
}
@@ -309,14 +310,15 @@
line-height: 1.5;
}
.nb-stdout, .nb-stderr {
.nb-stdout,
.nb-stderr {
white-space: pre-wrap;
margin: 1em 0;
padding: 0.1em 0.5em;
}
.nb-stderr {
background-color: #FAA;
background-color: #faa;
}
.nb-cell + .nb-cell {
@@ -338,9 +340,9 @@
.nb-raw-cell {
white-space: pre-wrap;
background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-family: Consolas, Liberation Mono, Menlo, monospace;
padding: 1em;
margin: .5em 0;
margin: 0.5em 0;
}
.nb-input:before,
@@ -369,7 +371,7 @@
}
}
}
.nb-output{
.nb-output {
&:before {
content: "Out [" attr(data-prompt-number) "]:";
}
@@ -418,7 +420,7 @@
.code-view {
* {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-family: Consolas, Liberation Mono, Menlo, monospace;
line-height: 20px;
}
@@ -457,12 +459,10 @@
li {
display: inline-block;
width: 100%;
padding-left: 5px;
&.active {
background: #ffffdd;
}
&:before {
content: ' ';
}
}
}
}
@@ -520,9 +520,9 @@
.branch-name {
display: inline-block;
padding: 3px 6px;
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
color: rgba(0,0,0,0.65);
background-color: rgba(209,227,237,0.45);
font: 12px Consolas, Liberation Mono, Menlo, monospace;
color: rgba(0, 0, 0, 0.65);
background-color: rgba(209, 227, 237, 0.45);
border-radius: 3px;
}
.new-branch-name-input {
@@ -546,7 +546,7 @@
&.options {
#interval {
width: 100px!important;
width: 100px !important;
min-width: 100px;
}
.danger {
@@ -584,12 +584,11 @@
overflow-x: auto;
}
}
}
}
&.view.issue {
.title {
padding-bottom: 0!important;
padding-bottom: 0 !important;
h1 {
font-weight: 300;
font-size: 2.3rem;
@@ -619,7 +618,7 @@
}
.pull-desc {
code {
color: #0166E6;
color: #0166e6;
}
}
.pull {
@@ -669,7 +668,7 @@
margin-top: 3px;
padding: 2px 5px;
font-size: 12px;
border: 1px solid rgba(0,0,0,0.1);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 3px;
}
.actions {
@@ -713,7 +712,7 @@
background: #f3f4f5;
.ui.images::after {
clear: both;
content: ' ';
content: " ";
display: block;
}
a {
@@ -727,7 +726,7 @@
max-width: 150px;
background-color: #fff;
&:before {
content:' ';
content: " ";
display: inline-block;
height: 100%;
vertical-align: middle;
@@ -760,7 +759,7 @@
}
textarea {
height: 200px;
font-family: "Consolas", monospace;
font-family: Consolas, Liberation Mono, Menlo, monospace;
}
}
@@ -840,7 +839,7 @@
}
textarea {
height: 200px;
font-family: "Consolas", monospace;
font-family: Consolas, Liberation Mono, Menlo, monospace;
}
}
}
@@ -848,10 +847,10 @@
.label.list {
list-style: none;
padding-top: 15px;
>.item {
> .item {
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px dashed #AAA;
border-bottom: 1px dashed #aaa;
a {
font-size: 15px;
padding-top: 5px;
@@ -876,7 +875,7 @@
> .item {
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px dashed #AAA;
border-bottom: 1px dashed #aaa;
> a {
padding-top: 5px;
padding-right: 10px;
@@ -897,7 +896,7 @@
.meta {
color: #999;
padding-top: 5px;
.issue-stats .octicon{
.issue-stats .octicon {
padding-left: 5px;
}
.overdue {
@@ -947,7 +946,7 @@
}
.filter.dropdown .menu {
margin-top: 1px!important;
margin-top: 1px !important;
}
&.diff {
@@ -979,7 +978,7 @@
}
}
&.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,11 +994,11 @@
list-style: none;
padding-bottom: 4px;
margin-bottom: 4px;
border-bottom: 1px dashed #DDD;
border-bottom: 1px dashed #ddd;
padding-left: 6px;
}
}
span.status{
span.status {
display: inline-block;
width: 12px;
height: 12px;
@@ -1048,7 +1047,7 @@
.file-body.file-code {
.lines-num {
text-align: right;
color: #A7A7A7;
color: #a7a7a7;
background: #fafafa;
width: 1%;
@@ -1058,7 +1057,7 @@
}
}
.lines-num-old {
border-right: 1px solid #DDD;
border-right: 1px solid #ddd;
}
}
.code-diff {
@@ -1079,9 +1078,11 @@
&::before {
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;
&:hover {
color: #383636;
@@ -1090,11 +1091,10 @@
}
tbody {
tr {
&.tag-code {
td {
background-color: #F0F0F0 !important;
border-color: #D2CECE!important;
background-color: #f0f0f0 !important;
border-color: #d2cece !important;
padding-top: 4px;
padding-bottom: 4px;
}
@@ -1115,7 +1115,6 @@
pre {
background-color: #eaffea !important;
border-color: #c1e9c1 !important;
}
}
td {
@@ -1179,7 +1178,7 @@
}
}
.clone.button:first-child {
border-radius: .28571429rem 0 0 .28571429rem;
border-radius: 0.28571429rem 0 0 0.28571429rem;
}
.ui.action.small.input {
width: 100%;
@@ -1194,11 +1193,11 @@
&.release {
#release-list {
border-top: 1px solid #DDD;
border-top: 1px solid #ddd;
margin-top: 20px;
padding-top: 15px;
>li {
> li {
list-style: none;
.meta,
@@ -1220,7 +1219,7 @@
}
}
.detail {
border-left: 1px solid #DDD;
border-left: 1px solid #ddd;
.author {
img {
@@ -1230,7 +1229,7 @@
.download {
margin-top: 20px;
>a {
> a {
.octicon {
margin-left: 5px;
margin-right: 5px;
@@ -1260,7 +1259,7 @@
left: -5px;
top: 40px;
border-radius: 6px;
border: 1px solid #FFF;
border: 1px solid #fff;
}
}
}
@@ -1299,7 +1298,7 @@
.item {
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #DDD;
border-bottom: 1px solid #ddd;
.ui.avatar {
float: left;
@@ -1327,7 +1326,7 @@
&.new {
.CodeMirror {
.CodeMirror-code {
font-family: "Consolas", monospace;
font-family: Consolas, Liberation Mono, Menlo, monospace;
.cm-comment {
background: inherit;
}
@@ -1351,7 +1350,12 @@
padding-left: 25px;
margin-left: -25px;
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
&:first-of-type {
margin-top: 0;
}
@@ -1365,12 +1369,12 @@
.collaborator.list {
padding: 0;
>.item {
> .item {
margin: 0;
line-height: 2em;
&:not(:last-child) {
border-bottom: 1px solid #DDD;
border-bottom: 1px solid #ddd;
}
}
}
@@ -1505,12 +1509,12 @@
.item {
padding: 10px 15px;
border-bottom: 1px solid #DDD;
border-bottom: 1px solid #ddd;
cursor: pointer;
&:hover {
background: rgba(0,0,0,.05)!important;
color: rgba(0,0,0,.95)!important;
background: rgba(0, 0, 0, 0.05) !important;
color: rgba(0, 0, 0, 0.95) !important;
}
img {
margin-right: 8px;
@@ -1522,10 +1526,10 @@
.issue.list {
list-style: none;
padding-top: 15px;
>.item {
> .item {
padding-top: 15px;
padding-bottom: 10px;
border-bottom: 1px dashed #AAA;
border-bottom: 1px dashed #aaa;
.title {
color: #444;
font-size: 15px;
@@ -1544,9 +1548,9 @@
color: #999;
a.milestone {
padding-left: 5px;
color: #999!important;
color: #999 !important;
&:hover {
color: #000!important;
color: #000 !important;
}
}
.assignee {
@@ -1565,8 +1569,8 @@
.dropzone {
width: 100%;
margin-bottom: 10px;
border: 2px dashed #0087F7;
box-shadow: none!important;
border: 2px dashed #0087f7;
box-shadow: none !important;
.dz-error-message {
top: 140px;
}
@@ -1701,7 +1705,8 @@
}
#avatar-arrow {
&:before, &:after {
&:before,
&:after {
right: 100%;
top: 20px;
border: solid transparent;
@@ -1712,7 +1717,7 @@
pointer-events: none;
}
&:before {
border-right-color: #D4D4D5;
border-right-color: #d4d4d5;
border-width: 9px;
margin-top: -9px;
}
@@ -1726,7 +1731,7 @@
#transfer-repo-modal,
#delete-repo-modal {
.ui.message {
width: 100%!important;
width: 100% !important;
}
}

View File

@@ -458,11 +458,11 @@
<dt>{{.i18n.Tr "admin.config.git.disable_diff_highlight"}}</dt>
<dd><i class="fa fa{{if .Git.DisableDiffHighlight}}-check{{end}}-square-o"></i></dd>
<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>
<dd>{{.Git.MaxGitDiffLineCharacters}}</dd>
<dd>{{.Git.MaxDiffLineChars}}</dd>
<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>
<dd><code>{{.Git.GCArgs}}</code></dd>

View File

@@ -60,7 +60,7 @@
<!-- Stylesheet -->
<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>
<style>
.dropdown:hover > .menu { display: block; }
@@ -70,7 +70,7 @@
<!-- JavaScript -->
<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>

View File

@@ -29,8 +29,7 @@
</tr>
</thead>
<tbody>
{{ $r:= List .Commits}}
{{range $r}}
{{range .Commits}}
<tr>
<td class="author">
{{if .User}}
@@ -47,7 +46,7 @@
{{else}}
<a rel="nofollow" class="ui sha label" href="{{AppSubURL}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">{{ShortSHA1 .ID.String}}</a>
{{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 class="grey text right aligned">{{TimeSince .Author.When $.Lang}}</td>
</tr>

View File

@@ -4,7 +4,7 @@
<div class="diff-detail-box diff-box">
<div>
<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">
<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>
@@ -14,19 +14,19 @@
{{range .Diff.Files}}
<li>
<div class="diff-counter count pull-right">
{{if not .IsBin}}
<span class="add" data-line="{{.Addition}}">{{.Addition}}</span>
{{if not .IsBinary}}
<span class="add" data-line="{{.NumAdditions}}">{{.NumAdditions}}</span>
<span class="bar">
<span class="pull-left add"></span>
<span class="pull-left del"></span>
</span>
<span class="del" data-line="{{.Deletion}}">{{.Deletion}}</span>
<span class="del" data-line="{{.NumDeletions}}">{{.NumDeletions}}</span>
{{else}}
<span>{{$.i18n.Tr "repo.diff.bin"}}</span>
{{end}}
</div>
<!-- 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>
</li>
{{end}}
@@ -40,12 +40,12 @@
{{$.i18n.Tr "repo.diff.file_suppressed"}}
<div class="diff-counter count ui left">
{{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="pull-left add"></span>
<span class="pull-left del"></span>
</span>
<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span>
<span class="del" data-line="{{.NumDeletions}}">- {{.NumDeletions}}</span>
{{end}}
</div>
<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}}">
<h4 class="ui top attached normal header">
<div class="diff-counter count ui left">
{{if $file.IsBin}}
{{if $file.IsBinary}}
{{$.i18n.Tr "repo.diff.bin"}}
{{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="pull-left add"></span>
<span class="pull-left del"></span>
</span>
<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span>
<span class="del" data-line="{{.NumDeletions}}">- {{.NumDeletions}}</span>
{{end}}
</div>
<span class="file">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}</span>
@@ -80,7 +80,7 @@
<div class="ui unstackable attached table segment">
{{if not $file.IsRenamed}}
{{$isImage := (call $.IsImageFile $file.Name)}}
{{if and $isImage}}
{{if $isImage}}
<div class="center">
<img src="{{$.RawPath}}/{{EscapePound .Name}}">
</div>
@@ -92,22 +92,22 @@
{{$highlightClass := $file.HighlightClass}}
{{range $j, $section := $file.Sections}}
{{range $k, $line := $section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .GetType 4}}
<tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .Type 4}}
<td class="lines-num"></td>
<td colspan="3" class="lines-code">
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre>
</td>
{{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 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 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 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>
{{end}}
</tr>

View File

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

View File

@@ -20,7 +20,7 @@
</div>
<div class="field">
<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 class="inline field">

View File

@@ -16,7 +16,7 @@
</div>
{{range .Hooks}}
<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>
<a class="text blue ui right" href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}"><i class="fa fa-pencil"></i></a>
</div>

View File

@@ -79,7 +79,7 @@
</div>
<div class="field">
<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>
</div>

View File

@@ -23,35 +23,28 @@
<td colspan="3"><i class="octicon octicon-mail-reply"></i><a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a></td>
</tr>
{{end}}
{{range $item := .Files}}
{{$entry := index $item 0}}
{{$commit := index $item 1}}
{{range .Files}}
<tr>
{{if $entry.IsSubModule}}
{{if .Submodule}}
<td>
<span class="octicon octicon-file-submodule"></span>
{{$refURL := $commit.RefURL AppURL $.BranchLink}}
{{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}}
<a href="{{InferSubmoduleURL .Submodule}}">{{.Entry.Name}} @ {{ShortSHA1 .Submodule.Commit}}</a>
</td>
{{else}}
<td class="name">
{{if $entry.IsLink}}
{{if .Entry.IsSymlink}}
<span class="octicon octicon-file-symlink-file"></span>
{{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}}
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}">{{$entry.Name}}</a>
<a href="{{EscapePound $.TreeLink}}/{{EscapePound .Entry.Name}}">{{.Entry.Name}}</a>
</td>
{{end}}
<td class="message collapsing has-emoji">
<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}}
<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}}
</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>
{{end}}
</tbody>