Преглед изворни кода

#1597 support pull requests in same repository

tags/v0.9.99
Unknwon пре 8 година
родитељ
комит
2d2d85bba4

+ 1
- 1
README.md Прегледај датотеку



![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)


##### Current version: 0.8.56
##### Current version: 0.8.57


| Web | UI | Preview | | Web | UI | Preview |
|:-------------:|:-------:|:-------:| |:-------------:|:-------:|:-------:|

+ 2
- 2
cmd/web.go Прегледај датотеку



// Check dependency version. // Check dependency version.
checkers := []VerChecker{ checkers := []VerChecker{
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.4.4.1029"},
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.2.0304"},
{"github.com/go-macaron/binding", binding.Version, "0.2.1"}, {"github.com/go-macaron/binding", binding.Version, "0.2.1"},
{"github.com/go-macaron/cache", cache.Version, "0.1.2"}, {"github.com/go-macaron/cache", cache.Version, "0.1.2"},
{"github.com/go-macaron/csrf", csrf.Version, "0.0.3"}, {"github.com/go-macaron/csrf", csrf.Version, "0.0.3"},
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"}, {"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
{"gopkg.in/ini.v1", ini.Version, "1.8.4"}, {"gopkg.in/ini.v1", ini.Version, "1.8.4"},
{"gopkg.in/macaron.v1", macaron.Version, "0.8.0"}, {"gopkg.in/macaron.v1", macaron.Version, "0.8.0"},
{"github.com/gogits/git-module", git.Version, "0.2.8"},
{"github.com/gogits/git-module", git.Version, "0.2.9"},
{"github.com/gogits/go-gogs-client", gogs.Version, "0.7.3"}, {"github.com/gogits/go-gogs-client", gogs.Version, "0.7.3"},
} }
for _, c := range checkers { for _, c := range checkers {

+ 1
- 1
gogs.go Прегледај датотеку

"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )


const APP_VER = "0.8.56.0304"
const APP_VER = "0.8.57.0304"


func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())

+ 1
- 1
models/pull.go Прегледај датотеку

// FIXME: could fail after user force push head repo, should we always force push here? // FIXME: could fail after user force push head repo, should we always force push here?
// FIXME: Only push branches that are actually updates? // FIXME: Only push branches that are actually updates?
func (pr *PullRequest) PushToBaseRepo() (err error) { func (pr *PullRequest) PushToBaseRepo() (err error) {
log.Trace("PushToBaseRepo[%[1]d]: pushing commits to base repo 'refs/pull/%[1]d/head'", pr.ID)
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo 'refs/pull/%d/head'", pr.BaseRepoID, pr.Index)


headRepoPath := pr.HeadRepo.RepoPath() headRepoPath := pr.HeadRepo.RepoPath()
headGitRepo, err := git.OpenRepository(headRepoPath) headGitRepo, err := git.OpenRepository(headRepoPath)

+ 5
- 0
models/repo.go Прегледај датотеку



func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "default_branch":
// FIXME: use models migration to solve all at once.
if len(repo.DefaultBranch) == 0 {
repo.DefaultBranch = "master"
}
case "num_closed_issues": case "num_closed_issues":
repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
case "num_closed_pulls": case "num_closed_pulls":

+ 10
- 7
models/user.go Прегледај датотеку



// IsAdminOfRepo returns true if user has admin or higher access of repository. // IsAdminOfRepo returns true if user has admin or higher access of repository.
func (u *User) IsAdminOfRepo(repo *Repository) bool { func (u *User) IsAdminOfRepo(repo *Repository) bool {
if err := repo.GetOwner(); err != nil {
log.Error(3, "GetOwner: %v", err)
return false
}

if repo.Owner.IsOrganization() {
if repo.MustOwner().IsOrganization() {
has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN) has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
if err != nil { if err != nil {
log.Error(3, "HasAccess: %v", err) log.Error(3, "HasAccess: %v", err)
return false
} }
return has return has
} }
return repo.IsOwnedBy(u.Id) return repo.IsOwnedBy(u.Id)
} }


// CanWriteTo returns true if user has write access to given repository.
func (u *User) CanWriteTo(repo *Repository) bool {
has, err := HasAccess(u, repo, ACCESS_MODE_WRITE)
if err != nil {
log.Error(3, "HasAccess: %v", err)
}
return has
}

// IsOrganization returns true if user is actually a organization. // IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool { func (u *User) IsOrganization() bool {
return u.Type == ORGANIZATION return u.Type == ORGANIZATION

+ 0
- 68
modules/log/database.go Прегледај датотеку

// 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 log

import (
"encoding/json"

"github.com/go-xorm/xorm"
)

type Log struct {
Id int64
Level int
Msg string `xorm:"TEXT"`
}

// DatabaseWriter implements LoggerInterface and is used to log into database.
type DatabaseWriter struct {
Driver string `json:"driver"`
Conn string `json:"conn"`
Level int `json:"level"`
x *xorm.Engine
}

func NewDatabase() LoggerInterface {
return &DatabaseWriter{Level: TRACE}
}

// init database writer with json config.
// config like:
// {
// "driver": "mysql"
// "conn":"root:root@tcp(127.0.0.1:3306)/gogs?charset=utf8",
// "level": 0
// }
// connection string is based on xorm.
func (d *DatabaseWriter) Init(jsonconfig string) (err error) {
if err = json.Unmarshal([]byte(jsonconfig), d); err != nil {
return err
}
d.x, err = xorm.NewEngine(d.Driver, d.Conn)
if err != nil {
return err
}
return d.x.Sync(new(Log))
}

// write message in database writer.
func (d *DatabaseWriter) WriteMsg(msg string, skip, level int) error {
if level < d.Level {
return nil
}

_, err := d.x.Insert(&Log{Level: level, Msg: msg})
return err
}

func (_ *DatabaseWriter) Flush() {
}

func (_ *DatabaseWriter) Destroy() {
}

func init() {
Register("database", NewDatabase)
}

+ 25
- 26
modules/middleware/repo.go Прегледај датотеку

ctx.Handle(500, "BaseRepo.GetOwner", err) ctx.Handle(500, "BaseRepo.GetOwner", err)
return return
} }

bsaeRepo := repo.BaseRepo
baseGitRepo, err := git.OpenRepository(models.RepoPath(bsaeRepo.Owner.Name, bsaeRepo.Name))
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return
}
if len(bsaeRepo.DefaultBranch) > 0 && baseGitRepo.IsBranchExist(bsaeRepo.DefaultBranch) {
ctx.Data["BaseDefaultBranch"] = bsaeRepo.DefaultBranch
} else {
baseBranches, err := baseGitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "GetBranches", err)
return
}
if len(baseBranches) > 0 {
ctx.Data["BaseDefaultBranch"] = baseBranches[0]
}
}
} }


func RepoAssignment(args ...bool) macaron.Handler { func RepoAssignment(args ...bool) macaron.Handler {
ctx.Data["Tags"] = tags ctx.Data["Tags"] = tags
ctx.Repo.Repository.NumTags = len(tags) ctx.Repo.Repository.NumTags = len(tags)


ctx.Data["Title"] = owner.Name + "/" + repo.Name
ctx.Data["Repository"] = repo
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
ctx.Data["IsRepositoryPusher"] = ctx.Repo.IsPusher()

if repo.IsFork { if repo.IsFork {
RetrieveBaseRepo(ctx, repo) RetrieveBaseRepo(ctx, repo)
if ctx.Written() { if ctx.Written() {
} }
} }


ctx.Data["Title"] = owner.Name + "/" + repo.Name
ctx.Data["Repository"] = repo
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
ctx.Data["IsRepositoryPusher"] = ctx.Repo.IsPusher()
ctx.Data["CanPullRequest"] = ctx.Repo.IsAdmin() && repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls()
// People who have push access and propose a new pull request.
if ctx.Repo.IsPusher() {
// Pull request is allowed if this is a fork repository
// and base repository accepts pull requests.
if repo.BaseRepo != nil {
if repo.BaseRepo.AllowsPulls() {
ctx.Data["CanPullRequest"] = true
ctx.Data["BaseRepo"] = repo.BaseRepo
}
} else {
// Or, this is repository accepts pull requests between branches.
if repo.AllowsPulls() {
ctx.Data["CanPullRequest"] = true
ctx.Data["BaseRepo"] = repo
ctx.Data["IsBetweenBranches"] = true
}
}
}


ctx.Data["DisableSSH"] = setting.SSH.Disabled ctx.Data["DisableSSH"] = setting.SSH.Disabled
ctx.Data["CloneLink"] = repo.CloneLink() ctx.Data["CloneLink"] = repo.CloneLink()

+ 45
- 22
routers/repo/pull.go Прегледај датотеку

} }


func ParseCompareInfo(ctx *middleware.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) { func ParseCompareInfo(ctx *middleware.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
// Get compare branch information.
// Get compared branches information
// format: <base branch>...[<head repo>:]<head branch>
// base<-head: master...head:feature
// same repo: master...feature
infos := strings.Split(ctx.Params("*"), "...") infos := strings.Split(ctx.Params("*"), "...")
if len(infos) != 2 { if len(infos) != 2 {
log.Trace("Not enough compared branches information: %s", infos)
ctx.Handle(404, "CompareAndPullRequest", nil) ctx.Handle(404, "CompareAndPullRequest", nil)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
baseBranch := infos[0] baseBranch := infos[0]
ctx.Data["BaseBranch"] = baseBranch ctx.Data["BaseBranch"] = baseBranch


var (
headUser *models.User
headBranch string
isSameRepo bool
err error
)

// If there is no head repository, it means pull request between same repository.
headInfos := strings.Split(infos[1], ":") headInfos := strings.Split(infos[1], ":")
if len(headInfos) != 2 {
ctx.Handle(404, "CompareAndPullRequest", nil)
return nil, nil, nil, nil, "", ""
}
headUsername := headInfos[0]
headBranch := headInfos[1]
ctx.Data["HeadBranch"] = headBranch
if len(headInfos) == 1 {
isSameRepo = true
headUser = ctx.Repo.Owner
headBranch = headInfos[0]


headUser, err := models.GetUserByName(headUsername)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Handle(404, "GetUserByName", nil)
} else {
ctx.Handle(500, "GetUserByName", err)
} else if len(headInfos) == 2 {
headUser, err = models.GetUserByName(headInfos[0])
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Handle(404, "GetUserByName", nil)
} else {
ctx.Handle(500, "GetUserByName", err)
}
return nil, nil, nil, nil, "", ""
} }
headBranch = headInfos[1]

} else {
ctx.Handle(404, "CompareAndPullRequest", nil)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
ctx.Data["HeadUser"] = headUser ctx.Data["HeadUser"] = headUser
ctx.Data["HeadBranch"] = headBranch
ctx.Data["IsBetweenBranches"] = isSameRepo


repo := ctx.Repo.Repository repo := ctx.Repo.Repository


return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }


// Check if current user has fork of repository.
// Check if current user has fork of repository or in the same repository.
headRepo, has := models.HasForkedRepo(headUser.Id, repo.ID) headRepo, has := models.HasForkedRepo(headUser.Id, repo.ID)
if !has || (!ctx.User.IsAdminOfRepo(headRepo) && !ctx.User.IsAdmin) {
if (!has && !isSameRepo) || (!ctx.User.CanWriteTo(headRepo) && !ctx.User.IsAdmin) {
ctx.Handle(404, "HasForkedRepo", nil) ctx.Handle(404, "HasForkedRepo", nil)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }


headGitRepo, err := git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return nil, nil, nil, nil, "", ""
var headGitRepo *git.Repository
if isSameRepo {
headRepo = ctx.Repo.Repository
headGitRepo = ctx.Repo.GitRepo
} else {
headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return nil, nil, nil, nil, "", ""
}
} }


// Check if head branch is valid. // Check if head branch is valid.
if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil { if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil {
ctx.Handle(500, "NewPullRequest", err) ctx.Handle(500, "NewPullRequest", err)
return return
}
if err := pullRequest.PushToBaseRepo(); err != nil {
} else if err := pullRequest.PushToBaseRepo(); err != nil {
ctx.Handle(500, "PushToBaseRepo", err) ctx.Handle(500, "PushToBaseRepo", err)
return return
} }

+ 1
- 1
templates/.VERSION Прегледај датотеку

0.8.56.0304
0.8.57.0304

+ 1
- 2
templates/repo/home.tmpl Прегледај датотеку

<div class="ui secondary menu"> <div class="ui secondary menu">
{{if .CanPullRequest}} {{if .CanPullRequest}}
<div class="fitted item"> <div class="fitted item">
{{ $baseRepo := .Repository.BaseRepo}}
<a href="{{AppSubUrl}}/{{$baseRepo.Owner.Name}}/{{$baseRepo.Name}}/compare/{{$.BaseDefaultBranch}}...{{$.Owner.Name}}:{{$.BranchName}}">
<a href="{{.BaseRepo.RepoLink}}/compare/{{.BaseRepo.DefaultBranch}}...{{if not .IsBetweenBranches}}{{$.Owner.Name}}:{{end}}{{$.BranchName}}">
<button class="ui green small button"><i class="octicon octicon-git-compare"></i></button> <button class="ui green small button"><i class="octicon octicon-git-compare"></i></button>
</a> </a>
</div> </div>

+ 1
- 1
templates/repo/issue/list.tmpl Прегледај датотеку

{{if .PageIsIssueList}} {{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new">{{.i18n.Tr "repo.issues.new"}}</a> <a class="ui green button" href="{{.RepoLink}}/issues/new">{{.i18n.Tr "repo.issues.new"}}</a>
{{else}} {{else}}
<a class="ui green button {{if not .HasForkedRepo}}disabled{{end}}" href="{{.RepoLink}}/compare/{{.BranchName}}...{{.SignedUserName}}:{{.BranchName}}">{{.i18n.Tr "repo.pulls.new"}}</a>
<a class="ui green button {{if not (or .CanPullRequest .HasForkedRepo)}}disabled{{end}}" href="{{.RepoLink}}/compare/{{.Repository.DefaultBranch}}...{{if not (eq .Owner.Name .SignedUserName)}}{{.SignedUserName}}:{{end}}{{.BranchName}}">{{.i18n.Tr "repo.pulls.new"}}</a>
{{end}} {{end}}
</div> </div>
</div> </div>

+ 2
- 2
templates/repo/pulls/compare.tmpl Прегледај датотеку

</div> </div>
<div class="scrolling menu"> <div class="scrolling menu">
{{range .Branches}} {{range .Branches}}
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{.}}...{{$.HeadUser.Name}}:{{$.HeadBranch}}">{{.}}</div>
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{.}}...{{if not $.IsBetweenBranches}}{{$.HeadUser.Name}}:{{end}}{{$.HeadBranch}}">{{.}}</div>
{{end}} {{end}}
</div> </div>
</div> </div>
</div> </div>
<div class="scrolling menu"> <div class="scrolling menu">
{{range .HeadBranches}} {{range .HeadBranches}}
<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{$.BaseBranch}}...{{$.HeadUser.Name}}:{{.}}">{{.}}</div>
<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{$.BaseBranch}}...{{if not $.IsBetweenBranches}}{{$.HeadUser.Name}}:{{end}}{{.}}">{{.}}</div>
{{end}} {{end}}
</div> </div>
</div> </div>

Loading…
Откажи
Сачувај