work on PR conversation

This commit is contained in:
Unknwon committed 2015-09-01 19:07:02 -04:00
1 parent 63fecac537
commit 8c046073a8
20 files changed
+784 -303

No files matched your search

+3 -2
View File
@@ -513,13 +513,14 @@ func runWeb(ctx *cli.Context) {
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
}, reqRepoAdmin, middleware.RepoRef())
m.Combo("/compare/*").Get(repo.CompareAndPullRequest)
m.Combo("/compare/*").Get(repo.CompareAndPullRequest).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
}, reqSignIn, middleware.RepoAssignment(true))
m.Group("/:username/:reponame", func() {
m.Get("/releases", middleware.RepoRef(), repo.Releases)
m.Get("/issues", repo.RetrieveLabels, repo.Issues)
m.Get("/issues/:index", repo.ViewIssue)
m.Get("/:type(issues|pulls)/:index", repo.ViewIssue)
m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", repo.Milestones)
m.Get("/pulls", repo.Pulls)
+3
View File
@@ -464,6 +464,9 @@ pulls.compare_changes = Compare Changes
pulls.compare_changes_desc = Compare two branches and make a pull request for changes.
pulls.no_results = No results found.
pulls.create = Create Pull Request
pulls.tab_conversation = Conversation
pulls.tab_commits = Commits
pulls.tab_files = Files changed
milestones.new = New Milestone
milestones.open_tab = %d Open
+21
View File
@@ -281,6 +281,27 @@ func (err ErrIssueNotExist) Error() string {
return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
}
// __________ .__ .__ __________ __
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
// | | | | / |_| |_| | \ ___< <_| | | /\ ___/ \___ \ | |
// |____| |____/|____/____/____|_ /\___ >__ |____/ \___ >____ > |__|
// \/ \/ |__| \/ \/
type ErrPullRepoNotExist struct {
ID int64
PullID int64
}
func IsErrPullRepoNotExist(err error) bool {
_, ok := err.(ErrPullRepoNotExist)
return ok
}
func (err ErrPullRepoNotExist) Error() string {
return fmt.Sprintf("pull repo does not exist [id: %d, pull_id: %d]", err.ID, err.PullID)
}
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
+159 -31
View File
@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"os"
"path"
@@ -21,6 +22,7 @@ import (
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
gouuid "github.com/gogits/gogs/modules/uuid"
)
@@ -44,9 +46,10 @@ type Issue struct {
MilestoneID int64
Milestone *Milestone `xorm:"-"`
AssigneeID int64
Assignee *User `xorm:"-"`
IsRead bool `xorm:"-"`
IsPull bool // Indicates whether is a pull request or not.
Assignee *User `xorm:"-"`
IsRead bool `xorm:"-"`
IsPull bool // Indicates whether is a pull request or not.
PullRepo *PullRepo `xorm:"-"`
IsClosed bool
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
@@ -92,6 +95,11 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) {
if err != nil {
log.Error(3, "GetUserByID[%d]: %v", i.ID, err)
}
case "is_pull":
i.PullRepo, err = GetPullRepoByPullID(i.ID)
if err != nil {
log.Error(3, "GetPullRepoByPullID[%d]: %v", i.ID, err)
}
case "created":
i.Created = regulateTimeZone(i.Created)
}
@@ -273,30 +281,11 @@ func (i *Issue) ChangeStatus(doer *User, isClosed bool) (err error) {
return sess.Commit()
}
// CreateIssue creates new issue with labels for repository.
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
// Check attachments.
attachments := make([]*Attachment, 0, len(uuids))
for _, uuid := range uuids {
attach, err := GetAttachmentByUUID(uuid)
if err != nil {
if IsErrAttachmentNotExist(err) {
continue
}
return fmt.Errorf("GetAttachmentByUUID[%s]: %v", uuid, err)
}
attachments = append(attachments, attach)
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
// It's caller's responsibility to create action.
func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
if _, err = e.Insert(issue); err != nil {
return err
}
if _, err = sess.Insert(issue); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `repository` SET num_issues=num_issues+1 WHERE id=?", issue.RepoID); err != nil {
} else if _, err = e.Exec("UPDATE `repository` SET num_issues=num_issues+1 WHERE id=?", issue.RepoID); err != nil {
return err
}
@@ -306,34 +295,62 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
continue
}
label, err = getLabelByID(sess, id)
label, err = getLabelByID(e, id)
if err != nil {
return err
}
if err = issue.addLabel(sess, label); err != nil {
if err = issue.addLabel(e, label); err != nil {
return fmt.Errorf("addLabel: %v", err)
}
}
if issue.MilestoneID > 0 {
if err = changeMilestoneAssign(sess, 0, issue); err != nil {
if err = changeMilestoneAssign(e, 0, issue); err != nil {
return err
}
}
if err = newIssueUsers(sess, repo, issue); err != nil {
if err = newIssueUsers(e, repo, issue); err != nil {
return err
}
// Check attachments.
attachments := make([]*Attachment, 0, len(uuids))
for _, uuid := range uuids {
attach, err := getAttachmentByUUID(e, uuid)
if err != nil {
if IsErrAttachmentNotExist(err) {
continue
}
return fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err)
}
attachments = append(attachments, attach)
}
for i := range attachments {
attachments[i].IssueID = issue.ID
// No assign value could be 0, so ignore AllCols().
if _, err = sess.Id(attachments[i].ID).Update(attachments[i]); err != nil {
if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
return fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err)
}
}
return nil
}
// NewIssue creates new issue with labels for repository.
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = newIssue(sess, repo, issue, labelIDs, uuids); err != nil {
return fmt.Errorf("newIssue: %v", err)
}
// Notify watchers.
act := &Action{
ActUserID: issue.Poster.Id,
@@ -813,6 +830,117 @@ func UpdateIssueUsersByMentions(uids []int64, iid int64) error {
return nil
}
// __________ .__ .__ __________ __
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
// | | | | / |_| |_| | \ ___< <_| | | /\ ___/ \___ \ | |
// |____| |____/|____/____/____|_ /\___ >__ |____/ \___ >____ > |__|
// \/ \/ |__| \/ \/
type PullRequestType int
const (
PULL_REQUEST_GOGS = iota
PLLL_ERQUEST_GIT
)
// PullRepo represents relation between pull request and repositories.
type PullRepo struct {
ID int64 `xorm:"pk autoincr"`
PullID int64 `xorm:"INDEX"`
HeadRepoID int64 `xorm:"UNIQUE(s)"`
HeadRepo *Repository `xorm:"-"`
BaseRepoID int64 `xorm:"UNIQUE(s)"`
HeadBarcnh string `xorm:"UNIQUE(s)"`
BaseBranch string `xorm:"UNIQUE(s)"`
MergeBase string `xorm:"VARCHAR(40)"`
Type PullRequestType
CanAutoMerge bool
}
func (pr *PullRepo) AfterSet(colName string, _ xorm.Cell) {
var err error
switch colName {
case "head_repo_id":
pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID)
if err != nil {
log.Error(3, "GetRepositoryByID[%d]: %v", pr.ID, err)
}
}
}
// NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(repo *Repository, pr *Issue, labelIDs []int64, uuids []string, pullRepo *PullRepo, patch []byte) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = newIssue(sess, repo, pr, labelIDs, uuids); err != nil {
return fmt.Errorf("newIssue: %v", err)
}
// Notify watchers.
act := &Action{
ActUserID: pr.Poster.Id,
ActUserName: pr.Poster.Name,
ActEmail: pr.Poster.Email,
OpType: PULL_REQUEST,
Content: fmt.Sprintf("%d|%s", pr.Index, pr.Name),
RepoID: repo.ID,
RepoUserName: repo.Owner.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
}
if err = notifyWatchers(sess, act); err != nil {
return err
}
// Test apply patch.
repoPath, err := repo.RepoPath()
if err != nil {
return fmt.Errorf("RepoPath: %v", err)
}
patchPath := path.Join(repoPath, "pulls", com.ToStr(pr.ID)+".patch")
os.MkdirAll(path.Dir(patchPath), os.ModePerm)
if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil {
return fmt.Errorf("save patch: %v", err)
}
defer os.Remove(patchPath)
stdout, stderr, err := process.ExecDir(-1, repoPath,
fmt.Sprintf("NewPullRequest(git apply --check): %d", repo.ID),
"git", "apply", "--check", "-v", patchPath)
if err != nil {
if strings.Contains(stderr, "fatal:") {
return fmt.Errorf("git apply --check: %v - %s", err, stderr)
}
}
pullRepo.CanAutoMerge = !strings.Contains(stdout, "error: patch failed:")
pullRepo.PullID = pr.ID
if _, err = sess.Insert(pullRepo); err != nil {
return fmt.Errorf("insert pull repo: %v", err)
}
return sess.Commit()
}
// GetPullRepoByPullID returns pull repo by given pull ID.
func GetPullRepoByPullID(pullID int64) (*PullRepo, error) {
pullRepo := new(PullRepo)
has, err := x.Where("pull_id=?", pullID).Get(pullRepo)
if err != nil {
return nil, err
} else if !has {
return nil, ErrPullRepoNotExist{0, pullID}
}
return pullRepo, nil
}
// .____ ___. .__
// | | _____ \_ |__ ____ | |
// | | \__ \ | __ \_/ __ \| |
+2 -2
View File
@@ -78,8 +78,8 @@ func init() {
tables = append(tables,
new(User), new(PublicKey), new(Oauth2), new(AccessToken),
new(Repository), new(DeployKey), new(Collaboration), new(Access),
new(Watch), new(Star), new(ForkInfo), new(Follow), new(Action),
new(Issue), new(Comment), new(Attachment), new(IssueUser),
new(Watch), new(Star), new(Follow), new(Action),
new(Issue), new(PullRepo), new(Comment), new(Attachment), new(IssueUser),
new(Label), new(IssueLabel), new(Milestone),
new(Mirror), new(Release), new(LoginSource), new(Webhook),
new(UpdateTask), new(HookTask),
-29
View File
@@ -160,7 +160,6 @@ type Repository struct {
IsFork bool `xorm:"NOT NULL DEFAULT false"`
ForkID int64
BaseRepo *Repository `xorm:"-"`
ForkInfo *ForkInfo `xorm:"-"`
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"`
@@ -168,15 +167,6 @@ type Repository struct {
func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "is_fork":
forkInfo := new(ForkInfo)
has, err := x.Where("repo_id=?", repo.ID).Get(forkInfo)
if err != nil {
log.Error(3, "get fork in[%d]: %v", repo.ID, err)
return
} else if has {
repo.ForkInfo = forkInfo
}
case "updated":
repo.Updated = regulateTimeZone(repo.Updated)
}
@@ -1047,8 +1037,6 @@ func DeleteRepository(uid, repoID int64) error {
if repo.IsFork {
if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil {
return fmt.Errorf("decrease fork count: %v", err)
} else if _, err = sess.Delete(&ForkInfo{RepoID: repo.ID}); err != nil {
return fmt.Errorf("delete fork info: %v", err)
}
}
@@ -1095,9 +1083,6 @@ func DeleteRepository(uid, repoID int64) error {
if _, err = x.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil {
log.Error(4, "reset 'fork_id' and 'is_fork': %v", err)
}
if _, err = x.Delete(&ForkInfo{ForkID: repo.ID}); err != nil {
log.Error(4, "clear fork infos: %v", err)
}
}
}
@@ -1669,13 +1654,6 @@ func IsStaring(uid, repoId int64) bool {
// \___ / \____/|__| |__|_ \
// \/ \/
type ForkInfo struct {
ID int64 `xorm:"pk autoincr"`
ForkID int64
RepoID int64 `xorm:"UNIQUE"`
StartCommitID string `xorm:"VARCHAR(40)"`
}
// HasForkedRepo checks if given user has already forked a repository with given ID.
func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
repo := new(Repository)
@@ -1709,13 +1687,6 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.ID); err != nil {
return nil, err
}
// else if _, err = sess.Insert(&ForkInfo{
// ForkID: oldRepo.ID,
// RepoID: repo.ID,
// StartCommitID: "",
// }); err != nil {
// return nil, fmt.Errorf("insert fork info: %v", err)
// }
oldRepoPath, err := oldRepo.RepoPath()
if err != nil {
+2 -2
View File
@@ -4364,7 +4364,7 @@ func confLocaleLocale_deDeIni() (*asset, error) {
return a, nil
}
var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\x7d\xfd\x72\xdc\x48\x8e\xe7\xff\x7c\x0a\xb6\x37\x7c\xee\x8e\x90\xcb\xd1\xdd\x71\x1f\xd1\x61\xbb\x4f\x96\xda\x1f\xb3\x96\xa5\xb5\xe4\x99\x9b\x73\x38\xd8\xac\x22\xab\x8a\xe3\x2a\xb2\x9a\x64\xb9\xac\xd9\xd8\x88\x7b\x8d\x7b\xbd\x7b\x92\x03\x7e\x00\xf2\x83\x64\xc9\xf6\xec\xc6\xdd\x3f\x12\x2b\x13\xf9\x85\x44\x22\x01\x24\x12\x99\xef\x76\x59\x51\x76\x8b\xf4\x49\x7a\x9a\xee\xf2\xaa\xde\x94\x5d\x97\x76\xe5\x66\xf9\x70\xdd\x74\x7d\x59\xa4\x2f\xaa\x9e\x7e\xb7\x9f\xaa\x45\x99\x24\xeb\x66\x5b\x12\xe8\x4b\xfa\x97\x14\x79\xb7\x9e\x37\x79\x5b\x50\xc2\xb9\x7d\x27\xe5\xe7\xdd\xa6\x69\x19\xe8\x37\xf9\x4a\xd6\xe5\x66\xc7\x65\xe8\x5f\xd2\x55\xab\x3a\xab\x6a\xfa\x79\x4d\x5f\xe9\xab\x3a\xe9\x9a\x45\x95\x6f\xb2\x20\x03\x09\x96\xff\x4b\xfa\x53\x5d\xa4\xd7\x7d\xb9\x4b\x1f\x77\xdb\x7c\xb3\x79\x9a\x77\x28\xd2\x97\x69\xbe\x58\x34\xfb\xba\x7f\xfc\x48\x32\xa4\xf2\x66\xdf\x5b\xed\x97\xfb\x5e\xd2\xf6\x3b\x4b\x7a\xb7\x4b\xda\x72\x55\xd1\xc0\x5a\x4a\x7a\xab\x9f\xc9\xa1\x9c\x77\x55\xcf\x9d\xfe\x8b\x7c\x25\x9f\xca\xb6\xab\x1a\xee\xcf\x9f\xe5\x2b\xd9\xe5\x2b\x06\xb8\xa2\x7f\x49\x5f\x6e\x77\x9b\x1c\x05\x6e\xf4\x33\xd9\xe4\xf5\x6a\x2f\x30\xaf\xf5\x33\x59\xb4\x25\x65\x65\x75\x79\xa0\xd4\x33\xfc\x48\xe9\xc7\x6c\x36\x4b\xf6\x84\xd3\x6c\xd7\x36\xcb\x6a\x53\x66\x79\x5d\x64\x5b\xc1\xda\x3b\x4a\x4f\x35\x3d\xa5\xf4\x94\xd3\x31\x8c\xb2\x20\x04\x65\x79\xa7\x63\xa1\xa9\x21\x7c\xe5\x5d\x82\xaa\xea\x7c\x6b\xa5\xf9\x33\x29\xb7\x79\xb5\xe1\x49\x78\xc8\x1f\xd4\xf9\xae\x3b\x34\x98\xaa\x2b\xfd\x24\x44\x64\xfd\xed\xae\x04\x1e\x1e\xde\xd0\x57\xb2\xc8\x77\xfd\x62\x9d\x73\x5f\xe5\x2b\x21\xa0\x5d\x43\x08\x69\xda\x5b\xc0\xd9\x8f\xa4\x69\x57\x79\x5d\xfd\x3d\xef\x05\x49\x97\xc1\xcf\x64\x5b\xb5\x6d\xc3\xf8\xbd\xc0\x47\x42\x23\xce\xb8\x1e\x4a\x79\x43\x98\x08\x6a\xe1\x9c\x6d\xb5\x6a\x05\x95\x9c\x79\x81\x5f\x5c\x0b\xe7\x2d\x9b\xf6\xa3\x66\x3c\xe7\xcf\x41\x51\xea\x84\xe6\xc6\xed\xe7\x35\x21\x5f\x73\x2f\xf0\x23\x02\xe8\x92\xbc\xd8\x12\x2a\x77\x79\x5d\x32\x8e\x4e\xf9\x17\xe1\x85\x7e\x25\x4a\x53\x59\x57\xf6\x7d\x55\xaf\x18\xd9\xa7\x92\x94\x5e\x6b\x52\x12\xe4\xb9\xb4\xdb\x66\xef\xa6\x93\xd2\xff\x4a\x3f\xd3\x2b\xf9\x29\x79\x41\x21\x64\xba\x92\x3c\x92\x2e\x5b\x96\x65\x21\x63\xe9\xd2\xe7\xf4\x9d\xec\xf6\x9b\x0d\x61\xed\x8f\x7d\xd9\xf5\x5c\xe8\x8a\x7e\xd3\xf8\xe5\x77\x52\x75\x1d\x7d\x50\xf2\x2b\x7c\x24\x34\x75\xf5\x02\x83\x39\xc3\x47\x92\xbc\xef\xca\xbc\x5d\xac\x3f\x24\xf2\x1f\x7d\xe5\x0f\xa6\xbd\x63\x93\xca\x84\xa4\x44\x24\x2d\x58\x03\xc9\xa2\x29\xf8\xc7\x19\xfd\xa3\xaa\xab\xba\xeb\x69\xc5\x7d\x48\xf4\x83\xc1\xe4\x4b\x26\xa0\xaf\x7a\x60\x41\x13\xb1\x7c\x3b\x9e\xc1\xf4\x79\xd5\x76\xfd\xc3\xbe\x22\x62\x7d\xbb\xaf\x13\x1e\x1f\xad\xb6\xac\x98\x1b\x13\x7a\xd1\x10\x8a\x90\xdc\xd2\xf8\x2e\x6e\xaf\xff\xe5\xf5\x49\x7a\x45\x9c\x68\xd5\x96\xf4\x9d\x52\x1d\xf4\x8f\xca\xfc\x3c\x4b\xa8\x94\xb5\x74\x9e\xf7\xf9\x3c\xef\x4a\x8f\x56\xce\x14\xea\x76\x79\xa0\x71\xe6\x6a\xe0\x60\x5d\x1f\x8d\x77\x6a\x85\x50\x1d\xba\xae\x5c\x1d\x6f\x78\x71\x51\x3a\x33\x35\x14\xbe\xda\x94\x9c\x4e\x55\xa5\xaf\xde\xbc\xb9\x3c\x7f\x96\x96\xf5\xaa\xaa\xcb\xf4\x50\xf5\xeb\x74\xdf\x2f\xff\x5b\xb6\x2a\xeb\xb2\x25\x1e\xb7\xa8\x52\x5a\x53\x2d\x51\x42\x4a\x84\x2d\x83\x9b\x25\x5d\xb7\xa1\xb5\x0f\xf4\x5e\x5f\xbf\x4e\x2f\x18\xc5\xbb\xbc\x5f\xa3\x23\xfd\x3a\xe9\xfe\xd8\x30\x8a\x5c\x83\x37\xeb\x32\x05\x95\x01\xa8\x59\x1a\x3e\xd2\x42\xfb\x38\x4b\xca\xb6\xcd\x88\x2d\xf5\xb7\x99\x16\xd6\xfa\x86\x90\x52\x05\x91\x4e\xdd\xf4\xe9\xbc\x4c\x51\x66\x96\x24\xd6\x61\xc3\xee\xe9\x6e\xb7\xa9\x16\xb2\xd6\x5f\x48\x9e\x47\x34\xef\x20\x8a\xa5\x10\x0e\x88\xb2\xbc\x00\x5d\xc4\x9e\x79\x3d\xa4\x11\x03\x41\xf9\x75\x49\x0c\x70\xbd\x5f\x09\xdb\xdb\x34\xfb\xe2\x3b\x50\xaa\xf5\xde\x13\x6a\xfa\xb6\xa1\x0e\x03\x3b\x0e\xc0\x37\x71\x4a\x14\xc7\x9b\x56\x5b\x6e\x1b\xe2\x2b\x8e\xd8\x2b\x22\xa8\x43\x45\x99\x34\xd2\x2e\xff\x44\xeb\xad\x6f\xd2\x7e\x5d\x75\x69\x41\xc4\xb6\xe0\x8a\x69\x69\xec\x69\xbb\x10\xb2\x20\x02\x15\xd2\xb0\xb4\x78\x0e\x00\xb5\xdd\x13\x35\xad\xa9\x32\xde\x8c\x78\xe7\xa4\x2a\xa7\xfa\x89\x21\x51\x3d\xa0\x6f\xa2\xdc\x86\xb8\x32\xf3\xcd\x73\x7c\xe8\xef\xb0\x7e\xea\x55\xbe\x5c\x52\xaf\x3a\xa2\x8a\x97\xe9\x62\xd3\x10\x49\xbd\x7b\xfb\xba\x63\x82\x59\x67\xbb\xa6\xc5\x36\x47\x59\x57\xf4\xe9\xd2\x02\x44\x33\x44\xbd\xdf\xce\xe9\xd7\x61\x5d\x11\x07\x00\xda\xb9\x04\xef\xe6\x94\x4a\x4d\xec\x3b\x9a\xc2\x93\x94\x48\x98\x46\x40\x28\x03\x01\xf0\x18\x8a\xaa\xcb\xe7\x34\xf7\x0c\xbe\xa4\x6d\x6b\xdf\x12\x59\xad\xfb\x7e\x67\x2d\xbf\xbc\xb9\xb9\x92\xa6\x5d\xea\x5d\x6d\xe7\x01\x65\x60\x0e\x36\xbc\xf1\xd6\x69\x53\xcf\x40\x24\xfb\x76\x33\xa0\x1f\x1a\xab\xe5\x1c\xc1\x0b\x77\xe1\x11\xff\xb9\xf6\xe8\x01\x9e\x3b\x92\x4e\x0e\xa0\x26\xc2\x71\x89\x0d\x90\x88\xba\xd9\x71\xbd\x01\x55\x5f\x6a\x82\x27\x65\x6c\x9a\x2e\x5f\xb6\x4e\xca\x85\xec\x13\xb0\xff\x2d\x0d\x58\xd9\xc8\xf5\x05\xa1\x01\xbc\x04\xa9\xcb\xb6\xd9\x52\xea\x73\xfa\xe7\x13\x7c\xf7\x2f\xb8\x3e\xc0\xe4\x45\x41\xfc\xad\x3b\x49\xdf\x3e\x3f\x4b\xff\xf3\xcf\x3f\xfd\x34\x4b\x5f\xf5\xbc\x12\x99\x38\xff\xc6\x44\x45\x9f\xb2\x87\x3b\x50\x62\x19\x3d\xd1\xdd\x3Line truncated
var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\x7d\xfd\x72\xdc\x48\x8e\xe7\xff\x7c\x0a\xb6\x37\x7c\xee\x8e\x90\xcb\xd1\xdd\x71\x1f\xd1\x61\xbb\x4f\x96\xda\x1f\xb3\x96\xa5\xb5\xe4\x99\x9b\x73\x38\xd8\xac\x22\xab\x8a\xe3\x2a\xb2\x9a\x64\xb9\xac\xd9\xd8\x88\x7b\x8d\x7b\xbd\x7b\x92\x03\x7e\x00\xf2\x83\x64\xc9\xf6\xec\xc6\xdd\x3f\x12\x2b\x13\xf9\x85\x44\x22\x01\x24\x12\x99\xef\x76\x59\x51\x76\x8b\xf4\x49\x7a\x9a\xee\xf2\xaa\xde\x94\x5d\x97\x76\xe5\x66\xf9\x70\xdd\x74\x7d\x59\xa4\x2f\xaa\x9e\x7e\xb7\x9f\xaa\x45\x99\x24\xeb\x66\x5b\x12\xe8\x4b\xfa\x97\x14\x79\xb7\x9e\x37\x79\x5b\x50\xc2\xb9\x7d\x27\xe5\xe7\xdd\xa6\x69\x19\xe8\x37\xf9\x4a\xd6\xe5\x66\xc7\x65\xe8\x5f\xd2\x55\xab\x3a\xab\x6a\xfa\x79\x4d\x5f\xe9\xab\x3a\xe9\x9a\x45\x95\x6f\xb2\x20\x03\x09\x96\xff\x4b\xfa\x53\x5d\xa4\xd7\x7d\xb9\x4b\x1f\x77\xdb\x7c\xb3\x79\x9a\x77\x28\xd2\x97\x69\xbe\x58\x34\xfb\xba\x7f\xfc\x48\x32\xa4\xf2\x66\xdf\x5b\xed\x97\xfb\x5e\xd2\xf6\x3b\x4b\x7a\xb7\x4b\xda\x72\x55\xd1\xc0\x5a\x4a\x7a\xab\x9f\xc9\xa1\x9c\x77\x55\xcf\x9d\xfe\x8b\x7c\x25\x9f\xca\xb6\xab\x1a\xee\xcf\x9f\xe5\x2b\xd9\xe5\x2b\x06\xb8\xa2\x7f\x49\x5f\x6e\x77\x9b\x1c\x05\x6e\xf4\x33\xd9\xe4\xf5\x6a\x2f\x30\xaf\xf5\x33\x59\xb4\x25\x65\x65\x75\x79\xa0\xd4\x33\xfc\x48\xe9\xc7\x6c\x36\x4b\xf6\x84\xd3\x6c\xd7\x36\xcb\x6a\x53\x66\x79\x5d\x64\x5b\xc1\xda\x3b\x4a\x4f\x35\x3d\xa5\xf4\x94\xd3\x31\x8c\xb2\x20\x04\x65\x79\xa7\x63\xa1\xa9\x21\x7c\xe5\x5d\x82\xaa\xea\x7c\x6b\xa5\xf9\x33\x29\xb7\x79\xb5\xe1\x49\x78\xc8\x1f\xd4\xf9\xae\x3b\x34\x98\xaa\x2b\xfd\x24\x44\x64\xfd\xed\xae\x04\x1e\x1e\xde\xd0\x57\xb2\xc8\x77\xfd\x62\x9d\x73\x5f\xe5\x2b\x21\xa0\x5d\x43\x08\x69\xda\x5b\xc0\xd9\x8f\xa4\x69\x57\x79\x5d\xfd\x3d\xef\x05\x49\x97\xc1\xcf\x64\x5b\xb5\x6d\xc3\xf8\xbd\xc0\x47\x42\x23\xce\xb8\x1e\x4a\x79\x43\x98\x08\x6a\xe1\x9c\x6d\xb5\x6a\x05\x95\x9c\x79\x81\x5f\x5c\x0b\xe7\x2d\x9b\xf6\xa3\x66\x3c\xe7\xcf\x41\x51\xea\x84\xe6\xc6\xed\xe7\x35\x21\x5f\x73\x2f\xf0\x23\x02\xe8\x92\xbc\xd8\x12\x2a\x77\x79\x5d\x32\x8e\x4e\xf9\x17\xe1\x85\x7e\x25\x4a\x53\x59\x57\xf6\x7d\x55\xaf\x18\xd9\xa7\x92\x94\x5e\x6b\x52\x12\xe4\xb9\xb4\xdb\x66\xef\xa6\x93\xd2\xff\x4a\x3f\xd3\x2b\xf9\x29\x79\x41\x21\x64\xba\x92\x3c\x92\x2e\x5b\x96\x65\x21\x63\xe9\xd2\xe7\xf4\x9d\xec\xf6\x9b\x0d\x61\xed\x8f\x7d\xd9\xf5\x5c\xe8\x8a\x7e\xd3\xf8\xe5\x77\x52\x75\x1d\x7d\x50\xf2\x2b\x7c\x24\x34\x75\xf5\x02\x83\x39\xc3\x47\x92\xbc\xef\xca\xbc\x5d\xac\x3f\x24\xf2\x1f\x7d\xe5\x0f\xa6\xbd\x63\x93\xca\x84\xa4\x44\x24\x2d\x58\x03\xc9\xa2\x29\xf8\xc7\x19\xfd\xa3\xaa\xab\xba\xeb\x69\xc5\x7d\x48\xf4\x83\xc1\xe4\x4b\x26\xa0\xaf\x7a\x60\x41\x13\xb1\x7c\x3b\x9e\xc1\xf4\x79\xd5\x76\xfd\xc3\xbe\x22\x62\x7d\xbb\xaf\x13\x1e\x1f\xad\xb6\xac\x98\x1b\x13\x7a\xd1\x10\x8a\x90\xdc\xd2\xf8\x2e\x6e\xaf\xff\xe5\xf5\x49\x7a\x45\x9c\x68\xd5\x96\xf4\x9d\x52\x1d\xf4\x8f\xca\xfc\x3c\x4b\xa8\x94\xb5\x74\x9e\xf7\xf9\x3c\xef\x4a\x8f\x56\xce\x14\xea\x76\x79\xa0\x71\xe6\x6a\xe0\x60\x5d\x1f\x8d\x77\x6a\x85\x50\x1d\xba\xae\x5c\x1d\x6f\x78\x71\x51\x3a\x33\x35\x14\xbe\xda\x94\x9c\x4e\x55\xa5\xaf\xde\xbc\xb9\x3c\x7f\x96\x96\xf5\xaa\xaa\xcb\xf4\x50\xf5\xeb\x74\xdf\x2f\xff\x5b\xb6\x2a\xeb\xb2\x25\x1e\xb7\xa8\x52\x5a\x53\x2d\x51\x42\x4a\x84\x2d\x83\x9b\x25\x5d\xb7\xa1\xb5\x0f\xf4\x5e\x5f\xbf\x4e\x2f\x18\xc5\xbb\xbc\x5f\xa3\x23\xfd\x3a\xe9\xfe\xd8\x30\x8a\x5c\x83\x37\xeb\x32\x05\x95\x01\xa8\x59\x1a\x3e\xd2\x42\xfb\x38\x4b\xca\xb6\xcd\x88\x2d\xf5\xb7\x99\x16\xd6\xfa\x86\x90\x52\x05\x91\x4e\xdd\xf4\xe9\xbc\x4c\x51\x66\x96\x24\xd6\x61\xc3\xee\xe9\x6e\xb7\xa9\x16\xb2\xd6\x5f\x48\x9e\x47\x34\xef\x20\x8a\xa5\x10\x0e\x88\xb2\xbc\x00\x5d\xc4\x9e\x79\x3d\xa4\x11\x03\x41\xf9\x75\x49\x0c\x70\xbd\x5f\x09\xdb\xdb\x34\xfb\xe2\x3b\x50\xaa\xf5\xde\x13\x6a\xfa\xb6\xa1\x0e\x03\x3b\x0e\xc0\x37\x71\x4a\x14\xc7\x9b\x56\x5b\x6e\x1b\xe2\x2b\x8e\xd8\x2b\x22\xa8\x43\x45\x99\x34\xd2\x2e\xff\x44\xeb\xad\x6f\xd2\x7e\x5d\x75\x69\x41\xc4\xb6\xe0\x8a\x69\x69\xec\x69\xbb\x10\xb2\x20\x02\x15\xd2\xb0\xb4\x78\x0e\x00\xb5\xdd\x13\x35\xad\xa9\x32\xde\x8c\x78\xe7\xa4\x2a\xa7\xfa\x89\x21\x51\x3d\xa0\x6f\xa2\xdc\x86\xb8\x32\xf3\xcd\x73\x7c\xe8\xef\xb0\x7e\xea\x55\xbe\x5c\x52\xaf\x3a\xa2\x8a\x97\xe9\x62\xd3\x10\x49\xbd\x7b\xfb\xba\x63\x82\x59\x67\xbb\xa6\xc5\x36\x47\x59\x57\xf4\xe9\xd2\x02\x44\x33\x44\xbd\xdf\xce\xe9\xd7\x61\x5d\x11\x07\x00\xda\xb9\x04\xef\xe6\x94\x4a\x4d\xec\x3b\x9a\xc2\x93\x94\x48\x98\x46\x40\x28\x03\x01\xf0\x18\x8a\xaa\xcb\xe7\x34\xf7\x0c\xbe\xa4\x6d\x6b\xdf\x12\x59\xad\xfb\x7e\x67\x2d\xbf\xbc\xb9\xb9\x92\xa6\x5d\xea\x5d\x6d\xe7\x01\x65\x60\x0e\x36\xbc\xf1\xd6\x69\x53\xcf\x40\x24\xfb\x76\x33\xa0\x1f\x1a\xab\xe5\x1c\xc1\x0b\x77\xe1\x11\xff\xb9\xf6\xe8\x01\x9e\x3b\x92\x4e\x0e\xa0\x26\xc2\x71\x89\x0d\x90\x88\xba\xd9\x71\xbd\x01\x55\x5f\x6a\x82\x27\x65\x6c\x9a\x2e\x5f\xb6\x4e\xca\x85\xec\x13\xb0\xff\x2d\x0d\x58\xd9\xc8\xf5\x05\xa1\x01\xbc\x04\xa9\xcb\xb6\xd9\x52\xea\x73\xfa\xe7\x13\x7c\xf7\x2f\xb8\x3e\xc0\xe4\x45\x41\xfc\xad\x3b\x49\xdf\x3e\x3f\x4b\xff\xf3\xcf\x3f\xfd\x34\x4b\x5f\xf5\xbc\x12\x99\x38\xff\xc6\x44\x45\x9f\xb2\x87\x3b\x50\x62\x19\x3d\xd1\xdd\x3Line truncated
func confLocaleLocale_enUsIniBytes() ([]byte, error) {
return bindataRead(
@@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 40911, mode: os.FileMode(420), modTime: time.Unix(1441119631, 0)}
info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 41009, mode: os.FileMode(420), modTime: time.Unix(1441146568, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2015 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 git
import (
"container/list"
"fmt"
"strings"
"time"
"github.com/Unknwon/com"
)
type PullRequestInfo struct {
MergeBase string
Commits *list.List
// Diff *Diff
NumFiles int
}
// GetPullRequestInfo generates and returns pull request information
// between base and head branches of repositories.
func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch string) (*PullRequestInfo, error) {
// Add a temporary remote.
tmpRemote := com.ToStr(time.Now().UnixNano())
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "remote", "add", "-f", tmpRemote, basePath)
if err != nil {
return nil, fmt.Errorf("add base as remote: %v", concatenateError(err, stderr))
}
defer func() {
com.ExecCmdDir(repo.Path, "git", "remote", "remove", tmpRemote)
}()
prInfo := new(PullRequestInfo)
var stdout string
remoteBranch := "remotes/" + tmpRemote + "/" + baseBranch
// Get merge base commit.
stdout, stderr, err = com.ExecCmdDir(repo.Path, "git", "merge-base", remoteBranch, headBranch)
if err != nil {
return nil, fmt.Errorf("get merge base: %v", concatenateError(err, stderr))
}
prInfo.MergeBase = strings.TrimSpace(stdout)
stdout, stderr, err = com.ExecCmdDir(repo.Path, "git", "log", remoteBranch+"..."+headBranch, prettyLogFormat)
if err != nil {
return nil, fmt.Errorf("list diff logs: %v", concatenateError(err, stderr))
}
prInfo.Commits, err = parsePrettyFormatLog(repo, []byte(stdout))
if err != nil {
return nil, fmt.Errorf("parsePrettyFormatLog: %v", err)
}
// Count number of changed files.
stdout, stderr, err = com.ExecCmdDir(repo.Path, "git", "diff", "--name-only", remoteBranch+"..."+headBranch)
if err != nil {
return nil, fmt.Errorf("list changed files: %v", concatenateError(err, stderr))
}
prInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1
return prInfo, nil
}
// GetPatch generates and returns patch data between given branches.
func (repo *Repository) GetPatch(basePath, baseBranch, headBranch string) ([]byte, error) {
// Add a temporary remote.
tmpRemote := com.ToStr(time.Now().UnixNano())
_, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "remote", "add", "-f", tmpRemote, basePath)
if err != nil {
return nil, fmt.Errorf("add base as remote: %v", concatenateError(err, string(stderr)))
}
defer func() {
com.ExecCmdDir(repo.Path, "git", "remote", "remove", tmpRemote)
}()
var stdout []byte
remoteBranch := "remotes/" + tmpRemote + "/" + baseBranch
stdout, stderr, err = com.ExecCmdDirBytes(repo.Path, "git", "diff", "-p", remoteBranch, headBranch)
if err != nil {
return nil, concatenateError(err, string(stderr))
}
return stdout, nil
}
+1 -1
View File
@@ -1 +1 @@
@font-face{font-family:octicons;src:url(../fonts/octicons.eot?#iefix&v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('embedded-opentype'),url(../fonts/octicons.woff?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('woff'),url(../fonts/octicons.ttf?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('truetype'),url(../fonts/octicons.svg?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d#octicons) format('svg');font-weight:400;font-style:normal}.mega-octicon,.octicon{font:normal normal normal 16px/1 octicons;display:inline-block;text-decoration:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mega-octicon{font-size:32px}.octicon-alert:before{content:'\f02d'}.octicon-arrow-down:before{content:'\f03f'}.octicon-arrow-left:before{content:'\f040'}.octicon-arrow-right:before{content:'\f03e'}.octicon-arrow-small-down:before{content:'\f0a0'}.octicon-arrow-small-left:before{content:'\f0a1'}.octicon-arrow-small-right:before{content:'\f071'}.octicon-arrow-small-up:before{content:'\f09f'}.octicon-arrow-up:before{content:'\f03d'}.octicon-beaker:before,.octicon-microscope:before{content:'\f0dd'}.octicon-bell:before{content:'\f0de'}.octicon-book:before{content:'\f007'}.octicon-bookmark:before{content:'\f07b'}.octicon-briefcase:before{content:'\f0d3'}.octicon-broadcast:before{content:'\f048'}.octicon-browser:before{content:'\f0c5'}.octicon-bug:before{content:'\f091'}.octicon-calendar:before{content:'\f068'}.octicon-check:before{content:'\f03a'}.octicon-checklist:before{content:'\f076'}.octicon-chevron-down:before{content:'\f0a3'}.octicon-chevron-left:before{content:'\f0a4'}.octicon-chevron-right:before{content:'\f078'}.octicon-chevron-up:before{content:'\f0a2'}.octicon-circle-slash:before{content:'\f084'}.octicon-circuit-board:before{content:'\f0d6'}.octicon-clippy:before{content:'\f035'}.octicon-clock:before{content:'\f046'}.octicon-cloud-download:before{content:'\f00b'}.octicon-cloud-upload:before{content:'\f00c'}.octicon-code:before{content:'\f05f'}.octicon-color-mode:before{content:'\f065'}.octicon-comment-add:before,.octicon-comment:before{content:'\f02b'}.octicon-comment-discussion:before{content:'\f04f'}.octicon-credit-card:before{content:'\f045'}.octicon-dash:before{content:'\f0ca'}.octicon-dashboard:before{content:'\f07d'}.octicon-database:before{content:'\f096'}.octicon-clone:before,.octicon-desktop-download:before{content:'\f0dc'}.octicon-device-camera:before{content:'\f056'}.octicon-device-camera-video:before{content:'\f057'}.octicon-device-desktop:before{content:'\f27c'}.octicon-device-mobile:before{content:'\f038'}.octicon-diff:before{content:'\f04d'}.octicon-diff-added:before{content:'\f06b'}.octicon-diff-ignored:before{content:'\f099'}.octicon-diff-modified:before{content:'\f06d'}.octicon-diff-removed:before{content:'\f06c'}.octicon-diff-renamed:before{content:'\f06e'}.octicon-ellipsis:before{content:'\f09a'}.octicon-eye-unwatch:before,.octicon-eye-watch:before,.octicon-eye:before{content:'\f04e'}.octicon-file-binary:before{content:'\f094'}.octicon-file-code:before{content:'\f010'}.octicon-file-directory:before{content:'\f016'}.octicon-file-media:before{content:'\f012'}.octicon-file-pdf:before{content:'\f014'}.octicon-file-submodule:before{content:'\f017'}.octicon-file-symlink-directory:before{content:'\f0b1'}.octicon-file-symlink-file:before{content:'\f0b0'}.octicon-file-text:before{content:'\f011'}.octicon-file-zip:before{content:'\f013'}.octicon-flame:before{content:'\f0d2'}.octicon-fold:before{content:'\f0cc'}.octicon-gear:before{content:'\f02f'}.octicon-gift:before{content:'\f042'}.octicon-gist:before{content:'\f00e'}.octicon-gist-secret:before{content:'\f08c'}.octicon-git-branch-create:before,.octicon-git-branch-delete:before,.octicon-git-branch:before{content:'\f020'}.octicon-git-commit:before{content:'\f01f'}.octicon-git-compare:before{content:'\f0ac'}.octicon-git-merge:before{content:'\f023'}.octicon-git-pull-request-abandoned:before,.octicon-git-pull-request:before{content:'\f009'}.octicon-globe:before{content:'\f0b6'}.octicon-graph:before{content:'\f043'}.octicon-heart:before{content:'\2665'}.octicon-history:before{content:'\f07e'}.octicon-home:before{content:'\f08d'}.octicon-horizontal-rule:before{content:'\f070'}.octicon-hubot:before{content:'\f09d'}.octicon-inbox:before{content:'\f0cf'}.octicon-info:before{content:'\f059'}.octicon-issue-closed:before{content:'\f028'}.octicon-issue-opened:before{content:'\f026'}.octicon-issue-reopened:before{content:'\f027'}.octicon-jersey:before{content:'\f019'}.octicon-key:before{content:'\f049'}.octicon-keyboard:before{content:'\f00d'}.octicon-law:before{content:'\f0d8'}.octicon-light-bulb:before{content:'\f000'}.octicon-link:before{content:'\f05c'}.octicon-link-external:before{content:'\f07f'}.octicon-list-ordered:before{content:'\f062'}.octicon-list-unordered:before{content:'\f061'}.octicon-location:before{content:'\f060'}.octicon-gLine truncated
@font-face{font-family:octicons;src:url(../fonts/octicons.eot?#iefix&v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('embedded-opentype'),url(../fonts/octicons.woff?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('woff'),url(../fonts/octicons.ttf?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('truetype'),url(../fonts/octicons.svg?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d#octicons) format('svg');font-weight:400;font-style:normal}.mega-octicon,.octicon{font:normal normal normal 16px/1 octicons;display:inline-block;text-decoration:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mega-octicon{font-size:32px}.octicon-alert:before{content:'\f02d'}.octicon-arrow-down:before{content:'\f03f'}.octicon-arrow-left:before{content:'\f040'}.octicon-arrow-right:before{content:'\f03e'}.octicon-arrow-small-down:before{content:'\f0a0'}.octicon-arrow-small-left:before{content:'\f0a1'}.octicon-arrow-small-right:before{content:'\f071'}.octicon-arrow-small-up:before{content:'\f09f'}.octicon-arrow-up:before{content:'\f03d'}.octicon-beaker:before,.octicon-microscope:before{content:'\f0dd'}.octicon-bell:before{content:'\f0de'}.octicon-book:before{content:'\f007'}.octicon-bookmark:before{content:'\f07b'}.octicon-briefcase:before{content:'\f0d3'}.octicon-broadcast:before{content:'\f048'}.octicon-browser:before{content:'\f0c5'}.octicon-bug:before{content:'\f091'}.octicon-calendar:before{content:'\f068'}.octicon-check:before{content:'\f03a'}.octicon-checklist:before{content:'\f076'}.octicon-chevron-down:before{content:'\f0a3'}.octicon-chevron-left:before{content:'\f0a4'}.octicon-chevron-right:before{content:'\f078'}.octicon-chevron-up:before{content:'\f0a2'}.octicon-circle-slash:before{content:'\f084'}.octicon-circuit-board:before{content:'\f0d6'}.octicon-clippy:before{content:'\f035'}.octicon-clock:before{content:'\f046'}.octicon-cloud-download:before{content:'\f00b'}.octicon-cloud-upload:before{content:'\f00c'}.octicon-code:before{content:'\f05f'}.octicon-color-mode:before{content:'\f065'}.octicon-comment-add:before,.octicon-comment:before{content:'\f02b'}.octicon-comment-discussion:before{content:'\f04f'}.octicon-credit-card:before{content:'\f045'}.octicon-dash:before{content:'\f0ca'}.octicon-dashboard:before{content:'\f07d'}.octicon-database:before{content:'\f096'}.octicon-clone:before,.octicon-desktop-download:before{content:'\f0dc'}.octicon-device-camera:before{content:'\f056'}.octicon-device-camera-video:before{content:'\f057'}.octicon-device-desktop:before{content:'\f27c'}.octicon-device-mobile:before{content:'\f038'}.octicon-diff:before{content:'\f04d'}.octicon-diff-added:before{content:'\f06b'}.octicon-diff-ignored:before{content:'\f099'}.octicon-diff-modified:before{content:'\f06d'}.octicon-diff-removed:before{content:'\f06c'}.octicon-diff-renamed:before{content:'\f06e'}.octicon-ellipsis:before{content:'\f09a'}.octicon-eye-unwatch:before,.octicon-eye-watch:before,.octicon-eye:before{content:'\f04e'}.octicon-file-binary:before{content:'\f094'}.octicon-file-code:before{content:'\f010'}.octicon-file-directory:before{content:'\f016'}.octicon-file-media:before{content:'\f012'}.octicon-file-pdf:before{content:'\f014'}.octicon-file-submodule:before{content:'\f017'}.octicon-file-symlink-directory:before{content:'\f0b1'}.octicon-file-symlink-file:before{content:'\f0b0'}.octicon-file-text:before{content:'\f011'}.octicon-file-zip:before{content:'\f013'}.octicon-flame:before{content:'\f0d2'}.octicon-fold:before{content:'\f0cc'}.octicon-gear:before{content:'\f02f'}.octicon-gift:before{content:'\f042'}.octicon-gist:before{content:'\f00e'}.octicon-gist-secret:before{content:'\f08c'}.octicon-git-branch-create:before,.octicon-git-branch-delete:before,.octicon-git-branch:before{content:'\f020'}.octicon-git-commit:before{content:'\f01f'}.octicon-git-compare:before{content:'\f0ac'}.octicon-git-merge:before{content:'\f023'}.octicon-git-pull-request-abandoned:before,.octicon-git-pull-request:before{content:'\f009'}.octicon-globe:before{content:'\f0b6'}.octicon-graph:before{content:'\f043'}.octicon-heart:before{content:'\2665'}.octicon-history:before{content:'\f07e'}.octicon-home:before{content:'\f08d'}.octicon-horizontal-rule:before{content:'\f070'}.octicon-hubot:before{content:'\f09d'}.octicon-inbox:before{content:'\f0cf'}.octicon-info:before{content:'\f059'}.octicon-issue-closed:before{content:'\f028'}.octicon-issue-opened:before{content:'\f026'}.octicon-issue-reopened:before{content:'\f027'}.octicon-jersey:before{content:'\f019'}.octicon-key:before{content:'\f049'}.octicon-keyboard:before{content:'\f00d'}.octicon-law:before{content:'\f0d8'}.octicon-light-bulb:before{content:'\f000'}.octicon-link:before{content:'\f05c'}.octicon-link-external:before{content:'\f07f'}.octicon-list-ordered:before{content:'\f062'}.octicon-list-unordered:before{content:'\f061'}.octicon-location:before{content:'\f060'}.octicon-gLine truncated
+16
View File
@@ -152,6 +152,22 @@
margin-top: 10px;
}
}
.pull {
&.tabular.menu {
margin-bottom: 10px;
.octicon {
margin-right: 5px;
}
}
&.tab.segment {
border: none;
padding: 0;
padding-top: 10px;
box-shadow: none;
background-color: inherit;
}
}
.comment-list {
&:before {
display: block;
+1
View File
@@ -297,6 +297,7 @@ func CompareDiff(ctx *middleware.Context) {
}
commits = models.ValidateCommitsWithEmails(commits)
ctx.Data["CommitRepoLink"] = ctx.Repo.RepoLink
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = commits.Len()
ctx.Data["BeforeCommitID"] = beforeCommitID
+119 -77
View File
@@ -18,6 +18,7 @@ import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/middleware"
@@ -185,11 +186,32 @@ func Issues(ctx *middleware.Context) {
}
func renderAttachmentSettings(ctx *middleware.Context) {
ctx.Data["RequireDropzone"] = true
ctx.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled
ctx.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes
ctx.Data["AttachmentMaxFiles"] = setting.AttachmentMaxFiles
}
func RetrieveRepoMilestonesAndAssignees(ctx *middleware.Context, repo *models.Repository) {
var err error
ctx.Data["OpenMilestones"], err = models.GetMilestones(repo.ID, -1, false)
if err != nil {
ctx.Handle(500, "GetMilestones: %v", err)
return
}
ctx.Data["ClosedMilestones"], err = models.GetMilestones(repo.ID, -1, true)
if err != nil {
ctx.Handle(500, "GetMilestones: %v", err)
return
}
ctx.Data["Assignees"], err = repo.GetAssignees()
if err != nil {
ctx.Handle(500, "GetAssignees: %v", err)
return
}
}
func RetrieveRepoMetas(ctx *middleware.Context, repo *models.Repository) []*models.Label {
if !ctx.Repo.IsAdmin() {
return nil
@@ -202,29 +224,17 @@ func RetrieveRepoMetas(ctx *middleware.Context, repo *models.Repository) []*mode
}
ctx.Data["Labels"] = labels
ctx.Data["OpenMilestones"], err = models.GetMilestones(repo.ID, -1, false)
if err != nil {
ctx.Handle(500, "GetMilestones: %v", err)
return nil
}
ctx.Data["ClosedMilestones"], err = models.GetMilestones(repo.ID, -1, true)
if err != nil {
ctx.Handle(500, "GetMilestones: %v", err)
RetrieveRepoMilestonesAndAssignees(ctx, repo)
if ctx.Written() {
return nil
}
ctx.Data["Assignees"], err = repo.GetAssignees()
if err != nil {
ctx.Handle(500, "GetAssignees: %v", err)
return nil
}
return labels
}
func NewIssue(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
ctx.Data["RequireDropzone"] = true
renderAttachmentSettings(ctx)
RetrieveRepoMetas(ctx, ctx.Repo.Repository)
@@ -235,62 +245,73 @@ func NewIssue(ctx *middleware.Context) {
ctx.HTML(200, ISSUE_NEW)
}
func ValidateRepoMetas(ctx *middleware.Context, form auth.CreateIssueForm) ([]int64, int64, int64) {
var (
repo = ctx.Repo.Repository
err error
)
labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository)
if ctx.Written() {
return nil, 0, 0
}
if !ctx.Repo.IsAdmin() {
return nil, 0, 0
}
// Check labels.
labelIDs := base.StringsToInt64s(strings.Split(form.LabelIDs, ","))
labelIDMark := base.Int64sToMap(labelIDs)
hasSelected := false
for i := range labels {
if labelIDMark[labels[i].ID] {
labels[i].IsChecked = true
hasSelected = true
}
}
ctx.Data["HasSelectedLabel"] = hasSelected
ctx.Data["label_ids"] = form.LabelIDs
ctx.Data["Labels"] = labels
// Check milestone.
milestoneID := form.MilestoneID
if milestoneID > 0 {
ctx.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID)
if err != nil {
ctx.Handle(500, "GetMilestoneByID: %v", err)
return nil, 0, 0
}
ctx.Data["milestone_id"] = milestoneID
}
// Check assignee.
assigneeID := form.AssigneeID
if assigneeID > 0 {
ctx.Data["Assignee"], err = repo.GetAssigneeByID(assigneeID)
if err != nil {
ctx.Handle(500, "GetAssigneeByID: %v", err)
return nil, 0, 0
}
ctx.Data["assignee_id"] = assigneeID
}
return labelIDs, milestoneID, assigneeID
}
func NewIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
ctx.Data["RequireDropzone"] = true
renderAttachmentSettings(ctx)
var (
repo = ctx.Repo.Repository
labelIDs []int64
milestoneID int64
assigneeID int64
attachments []string
err error
)
if ctx.Repo.IsAdmin() {
labels := RetrieveRepoMetas(ctx, repo)
if ctx.Written() {
return
}
// Check labels.
labelIDs = base.StringsToInt64s(strings.Split(form.LabelIDs, ","))
labelIDMark := base.Int64sToMap(labelIDs)
hasSelected := false
for i := range labels {
if labelIDMark[labels[i].ID] {
labels[i].IsChecked = true
hasSelected = true
}
}
ctx.Data["HasSelectedLabel"] = hasSelected
ctx.Data["label_ids"] = form.LabelIDs
ctx.Data["Labels"] = labels
// Check milestone.
milestoneID = form.MilestoneID
if milestoneID > 0 {
ctx.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID)
if err != nil {
ctx.Handle(500, "GetMilestoneByID: %v", err)
return
}
ctx.Data["milestone_id"] = milestoneID
}
// Check assignee.
assigneeID = form.AssigneeID
if assigneeID > 0 {
ctx.Data["Assignee"], err = repo.GetAssigneeByID(assigneeID)
if err != nil {
ctx.Handle(500, "GetAssigneeByID: %v", err)
return
}
ctx.Data["assignee_id"] = assigneeID
}
labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, form)
if ctx.Written() {
return
}
if setting.AttachmentEnabled {
@@ -332,7 +353,7 @@ func NewIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
// Mail watchers and mentions.
if setting.Service.EnableNotifyMail {
tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue)
tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, repo, issue)
if err != nil {
ctx.Handle(500, "SendIssueNotifyMail", err)
return
@@ -348,13 +369,13 @@ func NewIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
newTos = append(newTos, m)
}
if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner,
ctx.Repo.Repository, issue, models.GetUserEmailsByNames(newTos)); err != nil {
repo, issue, models.GetUserEmailsByNames(newTos)); err != nil {
ctx.Handle(500, "SendIssueMentionMail", err)
return
}
}
log.Trace("Issue created: %d/%d", ctx.Repo.Repository.ID, issue.ID)
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
}
@@ -421,6 +442,15 @@ func ViewIssue(ctx *middleware.Context) {
}
ctx.Data["Title"] = issue.Name
// Make sure type and URL matches.
if ctx.Params(":type") == "issues" && issue.IsPull {
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
return
} else if ctx.Params(":type") == "pulls" && !issue.IsPull {
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
return
}
if err = issue.GetPoster(); err != nil {
ctx.Handle(500, "GetPoster", err)
return
@@ -429,6 +459,30 @@ func ViewIssue(ctx *middleware.Context) {
repo := ctx.Repo.Repository
// Get more information if it's a pull request.
if issue.IsPull {
headRepoPath, err := issue.PullRepo.HeadRepo.RepoPath()
if err != nil {
ctx.Handle(500, "PullRepo.HeadRepo.RepoPath", err)
return
}
headGitRepo, err := git.OpenRepository(headRepoPath)
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return
}
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name),
issue.PullRepo.BaseBranch, issue.PullRepo.HeadBarcnh)
if err != nil {
ctx.Handle(500, "GetPullRequestInfo", err)
return
}
ctx.Data["NumCommits"] = prInfo.Commits.Len()
ctx.Data["NumFiles"] = prInfo.NumFiles
}
// Metas.
// Check labels.
if err = issue.GetLabels(); err != nil {
@@ -456,20 +510,8 @@ func ViewIssue(ctx *middleware.Context) {
// Check milestone and assignee.
if ctx.Repo.IsAdmin() {
ctx.Data["OpenMilestones"], err = models.GetMilestones(repo.ID, -1, false)
if err != nil {
ctx.Handle(500, "GetMilestones: %v", err)
return
}
ctx.Data["ClosedMilestones"], err = models.GetMilestones(repo.ID, -1, true)
if err != nil {
ctx.Handle(500, "GetMilestones: %v", err)
return
}
ctx.Data["Assignees"], err = repo.GetAssignees()
if err != nil {
ctx.Handle(500, "GetAssignees: %v", err)
RetrieveRepoMilestonesAndAssignees(ctx, repo)
if ctx.Written() {
return
}
}
+203 -25
View File
@@ -5,9 +5,11 @@
package repo
import (
"fmt"
"path"
"strings"
"github.com/Unknwon/com"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base"
@@ -124,17 +126,19 @@ func ForkPost(ctx *middleware.Context, form auth.CreateRepoForm) {
ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
}
func CompareAndPullRequest(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
ctx.Data["PageIsComparePull"] = true
func Pulls(ctx *middleware.Context) {
ctx.Data["IsRepoToolbarPulls"] = true
ctx.HTML(200, PULLS)
}
repo := ctx.Repo.Repository
// func ViewPull
func ParseCompareInfo(ctx *middleware.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
// Get compare branch information.
infos := strings.Split(ctx.Params("*"), "...")
if len(infos) != 2 {
ctx.Handle(404, "CompareAndPullRequest", nil)
return
return nil, nil, nil, nil, "", ""
}
baseBranch := infos[0]
@@ -143,47 +147,221 @@ func CompareAndPullRequest(ctx *middleware.Context) {
headInfos := strings.Split(infos[1], ":")
if len(headInfos) != 2 {
ctx.Handle(404, "CompareAndPullRequest", nil)
return
return nil, nil, nil, nil, "", ""
}
headUser := headInfos[0]
headUsername := headInfos[0]
headBranch := headInfos[1]
ctx.Data["HeadBranch"] = headBranch
// TODO: check if branches are valid.
fmt.Println(baseBranch, headUser, headBranch)
// TODO: add organization support
// Check if current user has fork of repository.
headRepo, has := models.HasForkedRepo(ctx.User.Id, repo.ID)
if !has {
ctx.Handle(404, "HasForkedRepo", nil)
return
headUser, err := models.GetUserByName(headUsername)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Handle(404, "GetUserByName", nil)
} else {
ctx.Handle(500, "GetUserByName", err)
}
return nil, nil, nil, nil, "", ""
}
headGitRepo, err := git.OpenRepository(models.RepoPath(ctx.User.Name, headRepo.Name))
repo := ctx.Repo.Repository
// Check if base branch is valid.
if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
ctx.Handle(404, "IsBranchExist", nil)
return nil, nil, nil, nil, "", ""
}
// Check if current user has fork of repository.
headRepo, has := models.HasForkedRepo(headUser.Id, repo.ID)
if !has || !ctx.User.IsAdminOfRepo(headRepo) {
ctx.Handle(404, "HasForkedRepo", 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
return nil, nil, nil, nil, "", ""
}
// Check if head branch is valid.
if !headGitRepo.IsBranchExist(headBranch) {
ctx.Handle(404, "IsBranchExist", nil)
return nil, nil, nil, nil, "", ""
}
headBranches, err := headGitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "GetBranches", err)
return
return nil, nil, nil, nil, "", ""
}
ctx.Data["HeadBranches"] = headBranches
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name), baseBranch, headBranch)
if err != nil {
ctx.Handle(500, "GetPullRequestInfo", err)
return nil, nil, nil, nil, "", ""
}
ctx.Data["BeforeCommitID"] = prInfo.MergeBase
return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch
}
func PrepareCompareDiff(
ctx *middleware.Context,
headUser *models.User,
headRepo *models.Repository,
headGitRepo *git.Repository,
prInfo *git.PullRequestInfo,
baseBranch, headBranch string) {
var (
repo = ctx.Repo.Repository
err error
)
// Get diff information.
ctx.Data["CommitRepoLink"], err = headRepo.RepoLink()
if err != nil {
ctx.Handle(500, "RepoLink", err)
return
}
headCommitID, err := headGitRepo.GetCommitIdOfBranch(headBranch)
if err != nil {
ctx.Handle(500, "GetCommitIdOfBranch", err)
return
}
ctx.Data["AfterCommitID"] = headCommitID
diff, err := models.GetDiffRange(models.RepoPath(headUser.Name, headRepo.Name),
prInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines)
if err != nil {
ctx.Handle(500, "GetDiffRange", err)
return
}
ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
headCommit, err := headGitRepo.GetCommit(headCommitID)
if err != nil {
ctx.Handle(500, "GetCommit", err)
return
}
isImageFile := func(name string) bool {
blob, err := headCommit.GetBlobByPath(name)
if err != nil {
return false
}
dataRc, err := blob.Data()
if err != nil {
return false
}
buf := make([]byte, 1024)
n, _ := dataRc.Read(buf)
if n > 0 {
buf = buf[:n]
}
_, isImage := base.IsImageFile(buf)
return isImage
}
prInfo.Commits = models.ValidateCommitsWithEmails(prInfo.Commits)
ctx.Data["Commits"] = prInfo.Commits
ctx.Data["CommitCount"] = prInfo.Commits.Len()
ctx.Data["Username"] = headUser.Name
ctx.Data["Reponame"] = headRepo.Name
ctx.Data["IsImageFile"] = isImageFile
ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(headUser.Name, repo.Name, "src", headCommitID)
ctx.Data["BeforeSourcePath"] = setting.AppSubUrl + "/" + path.Join(headUser.Name, repo.Name, "src", prInfo.MergeBase)
ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(headUser.Name, repo.Name, "raw", headCommitID)
}
func CompareAndPullRequest(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
ctx.Data["PageIsComparePull"] = true
ctx.Data["IsDiffCompare"] = true
renderAttachmentSettings(ctx)
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
if ctx.Written() {
return
}
PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch)
if ctx.Written() {
return
}
// Setup information for new form.
RetrieveRepoMetas(ctx, ctx.Repo.Repository)
if ctx.Written() {
return
}
// Get diff information.
ctx.HTML(200, COMPARE_PULL)
}
func Pulls(ctx *middleware.Context) {
ctx.Data["IsRepoToolbarPulls"] = true
ctx.HTML(200, PULLS)
func CompareAndPullRequestPost(ctx *middleware.Context, form auth.CreateIssueForm) {
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
ctx.Data["PageIsComparePull"] = true
ctx.Data["IsDiffCompare"] = true
renderAttachmentSettings(ctx)
var (
repo = ctx.Repo.Repository
attachments []string
)
_, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
if ctx.Written() {
return
}
patch, err := headGitRepo.GetPatch(models.RepoPath(repo.Owner.Name, repo.Name), baseBranch, headBranch)
if err != nil {
ctx.Handle(500, "GetPatch", err)
return
}
labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, form)
if ctx.Written() {
return
}
if setting.AttachmentEnabled {
attachments = form.Attachments
}
if ctx.HasError() {
ctx.HTML(200, COMPARE_PULL)
return
}
pr := &models.Issue{
RepoID: repo.ID,
Index: int64(repo.NumIssues) + 1,
Name: form.Title,
PosterID: ctx.User.Id,
Poster: ctx.User,
MilestoneID: milestoneID,
AssigneeID: assigneeID,
IsPull: true,
Content: form.Content,
}
if err := models.NewPullRequest(repo, pr, labelIDs, attachments, &models.PullRepo{
HeadRepoID: headRepo.ID,
BaseRepoID: repo.ID,
HeadBarcnh: headBranch,
BaseBranch: baseBranch,
MergeBase: prInfo.MergeBase,
Type: models.PULL_REQUEST_GOGS,
}, patch); err != nil {
ctx.Handle(500, "NewPullRequest", err)
return
}
log.Trace("Pull request created: %d/%d", repo.ID, pr.ID)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
}
+6 -5
View File
@@ -10,9 +10,11 @@
</form>
</div>
{{else if .IsDiffCompare}}
<a href="{{$.RepoLink}}/commit/{{.BeforeCommitID}}" class="ui green sha label">{{ShortSha .BeforeCommitID}}</a> ... <a href="{{$.RepoLink}}/commit/{{.AfterCommitID}}" class="ui green sha label">{{ShortSha .AfterCommitID}}</a>
<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID}}" class="ui green sha label">{{ShortSha .BeforeCommitID}}</a> ... <a href="{{$.CommitRepoLink}}/commit/{{.AfterCommitID}}" class="ui green sha label">{{ShortSha .AfterCommitID}}</a>
{{end}}
</h4>
{{if .Commits}}
<div class="ui attached table segment">
<table class="ui very basic striped commits table">
<thead>
@@ -24,9 +26,7 @@
</tr>
</thead>
<tbody>
{{ $username := .Username}}
{{ $reponame := .Reponame}}
{{ $r:= List .Commits}}
{{ $r:= List .Commits}}
{{range $r}}
<tr>
<td class="author">
@@ -36,7 +36,7 @@
<img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/>&nbsp;&nbsp;{{.Author.Name}}
{{end}}
</td>
<td class="sha"><a rel="nofollow" class="ui green sha label" href="{{AppSubUrl}}/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td>
<td class="sha"><a rel="nofollow" class="ui green sha label" href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td>
<td class="message"><span class="text truncate">{{RenderCommitMessage .Summary $.RepoLink}}</span></td>
<td class="date">{{TimeSince .Author.When $.Lang}}</td>
</tr>
@@ -44,6 +44,7 @@
</tbody>
</table>
</div>
{{end}}
{{with .Page}}
{{if gt .TotalPages 1}}
+1 -92
View File
@@ -41,98 +41,7 @@
</div>
{{end}}
{{if .DiffNotAvailable}}
<h4>{{.i18n.Tr "repo.diff.data_not_available"}}</h4>
{{else}}
<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}}
<div class="ui right">
<a class="ui tiny basic black toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
</div>
</div>
<ol class="detail-files hide" id="diff-files">
{{range .Diff.Files}}
<li>
<div class="diff-counter count pull-right">
{{if not .IsBin}}
<span class="add" data-line="{{.Addition}}">{{.Addition}}</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>
{{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 .Type}} poping up" data-content="{{DiffTypeToStr .Type}}" data-variation="inverted tiny" data-position="right center">&nbsp;</span>
<a class="file" href="#diff-{{.Index}}">{{.Name}}</a>
</li>
{{end}}
</ol>
</div>
{{range $i, $file := .Diff.Files}}
<div class="diff-file-box diff-box file-content" id="diff-{{.Index}}">
<h4 class="ui top attached normal header">
<div class="diff-counter count ui left">
{{if not $file.IsBin}}
<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</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>
{{else}}
{{$.i18n.Tr "repo.diff.bin"}}
{{end}}
</div>
<span class="file">{{$file.Name}}</span>
<div class="ui right">
{{if $file.IsDeleted}}
<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
{{else}}
<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
{{end}}
</div>
</h4>
<div class="ui attached table segment">
{{$isImage := (call $.IsImageFile $file.Name)}}
{{if $isImage}}
<div class="center">
<img src="{{$.RawPath}}/{{EscapePound .Name}}">
</div>
{{else}}
<div class="file-body file-code code-view code-diff">
<table>
<tbody>
{{range .Sections}}
{{range $k, $line := .Lines}}
<tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$k}} ol-{{$k}}">
<td class="lines-num lines-num-old">
<span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
</td>
<td class="lines-num lines-num-new">
<span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
</td>
<td class="lines-code">
<pre>{{$line.Content}}</pre>
</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
{{end}}
</div>
</div>
<br>
{{end}}
{{end}}
{{template "repo/diff_box" .}}
</div>
</div>
{{template "base/footer" .}}
+92
View File
@@ -0,0 +1,92 @@
{{if .DiffNotAvailable}}
<h4>{{.i18n.Tr "repo.diff.data_not_available"}}</h4>
{{else}}
<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}}
<div class="ui right">
<a class="ui tiny basic black toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
</div>
</div>
<ol class="detail-files hide" id="diff-files">
{{range .Diff.Files}}
<li>
<div class="diff-counter count pull-right">
{{if not .IsBin}}
<span class="add" data-line="{{.Addition}}">{{.Addition}}</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>
{{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 .Type}} poping up" data-content="{{DiffTypeToStr .Type}}" data-variation="inverted tiny" data-position="right center">&nbsp;</span>
<a class="file" href="#diff-{{.Index}}">{{.Name}}</a>
</li>
{{end}}
</ol>
</div>
{{range $i, $file := .Diff.Files}}
<div class="diff-file-box diff-box file-content" id="diff-{{.Index}}">
<h4 class="ui top attached normal header">
<div class="diff-counter count ui left">
{{if not $file.IsBin}}
<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</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>
{{else}}
{{$.i18n.Tr "repo.diff.bin"}}
{{end}}
</div>
<span class="file">{{$file.Name}}</span>
<div class="ui right">
{{if $file.IsDeleted}}
<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
{{else}}
<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
{{end}}
</div>
</h4>
<div class="ui attached table segment">
{{$isImage := (call $.IsImageFile $file.Name)}}
{{if $isImage}}
<div class="center">
<img src="{{$.RawPath}}/{{EscapePound .Name}}">
</div>
{{else}}
<div class="file-body file-code code-view code-diff">
<table>
<tbody>
{{range .Sections}}
{{range $k, $line := .Lines}}
<tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$k}} ol-{{$k}}">
<td class="lines-num lines-num-old">
<span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
</td>
<td class="lines-num lines-num-new">
<span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
</td>
<td class="lines-code">
<pre>{{$line.Content}}</pre>
</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
{{end}}
</div>
</div>
<br>
{{end}}
{{end}}
+24
View File
@@ -9,7 +9,31 @@
</div>
</div>
<div class="ui divider"></div>
{{if .Issue.IsPull}}
{{template "repo/issue/view_title" .}}
<div class="ui top attached pull tabular menu">
<a class="item active" href="{{.RepoLink}}/pulls/{{.Issue.Index}}">
<span class="octicon octicon-comment-discussion"></span>
{{$.i18n.Tr "repo.pulls.tab_conversation"}}
<span class="ui label">{{.Issue.NumComments}}</span>
</a>
<a class="item" href="{{.RepoLink}}/pulls/{{.Issue.Index}}/commits">
<span class="octicon octicon-git-commit"></span>
{{$.i18n.Tr "repo.pulls.tab_commits"}}
<span class="ui label">{{.NumCommits}}</span>
</a>
<a class="item" href="{{.RepoLink}}/pulls/{{.Issue.Index}}/files">
<span class="octicon octicon-diff"></span>
{{$.i18n.Tr "repo.pulls.tab_files"}}
<span class="ui label">{{.NumFiles}}</span>
</a>
</div>
<div class="ui bottom attached tab pull segment active" data-tab="request-{{.ID}}">
{{template "repo/issue/view_content" .}}
</div>
{{else}}
{{template "repo/issue/view_content" .}}
{{end}}
</div>
</div>
{{template "base/footer" .}}
+7 -37
View File
@@ -4,41 +4,11 @@
{{template "base/alert" .}}
</div>
{{end}}
<div class="sixteen wide column title">
<div class="ui grid">
<h1 class="twelve wide column">
<span class="index">#{{.Issue.Index}}</span> <span id="issue-title">{{.Issue.Name}}</span>
<div id="edit-title-input" class="ui input" style="display: none">
<input value="{{.Issue.Name}}">
</div>
</h1>
{{if .IsIssueOwner}}
<div class="four wide column">
<div class="edit-zone text right">
<div id="edit-title" class="ui basic green not-in-edit button">{{.i18n.Tr "repo.issues.edit"}}</div>
<div id="cancel-edit-title" class="ui basic blue in-edit button" style="display: none">{{.i18n.Tr "repo.issues.cancel"}}</div>
<div id="save-edit-title" class="ui green in-edit button" style="display: none" data-update-url="{{.Link}}/title">{{.i18n.Tr "repo.issues.save"}}</div>
</div>
</div>
{{end}}
</div>
{{if .Issue.IsClosed}}
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
{{else}}
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
{{end}}
{{ $createdStr:= TimeSince .Issue.Created $.Lang }}
<span class="time-desc">
{{if gt .Issue.Poster.Id 0}}
{{$.i18n.Tr "repo.issues.opened_by" $createdStr .Issue.Poster.HomeLink .Issue.Poster.Name | Safe}}
{{else}}
{{$.i18n.Tr "repo.issues.opened_by_fake" $createdStr .Issue.Poster.Name | Safe}}
{{end}}
·
{{$.i18n.Tr "repo.issues.num_comments" .Issue.NumComments}}
</span>
<div class="ui divider"></div>
</div>
{{if not .Issue.IsPull}}
{{template "repo/issue/view_title" .}}
{{end}}
{{ $createdStr:= TimeSince .Issue.Created $.Lang }}
<div class="twelve wide column comment-list">
<ui class="ui comments">
<div class="comment">
@@ -63,7 +33,7 @@
{{end}}
</div>
<div class="raw-content hide">{{.Issue.Content}}</div>
<div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{.Link}}/content" data-context="{{.RepoLink}}"></div>
<div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}"></div>
</div>
{{if .Issue.Attachments}}
<div class="ui bottom attached segment">
@@ -167,7 +137,7 @@
<img src="{{.SignedUser.AvatarLink}}">
</a>
<div class="content">
<form class="ui segment form" id="comment-form" action="{{.Link}}/comments" method="post">
<form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
{{template "repo/issue/comment_tab" .}}
{{.CsrfTokenHtml}}
<input id="status" name="status" type="hidden">
+35
View File
@@ -0,0 +1,35 @@
<div class="sixteen wide column title">
<div class="ui grid">
<h1 class="twelve wide column">
<span class="index">#{{.Issue.Index}}</span> <span id="issue-title">{{.Issue.Name}}</span>
<div id="edit-title-input" class="ui input" style="display: none">
<input value="{{.Issue.Name}}">
</div>
</h1>
{{if .IsIssueOwner}}
<div class="four wide column">
<div class="edit-zone text right">
<div id="edit-title" class="ui basic green not-in-edit button">{{.i18n.Tr "repo.issues.edit"}}</div>
<div id="cancel-edit-title" class="ui basic blue in-edit button" style="display: none">{{.i18n.Tr "repo.issues.cancel"}}</div>
<div id="save-edit-title" class="ui green in-edit button" style="display: none" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title">{{.i18n.Tr "repo.issues.save"}}</div>
</div>
</div>
{{end}}
</div>
{{if .Issue.IsClosed}}
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
{{else}}
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
{{end}}
{{ $createdStr:= TimeSince .Issue.Created $.Lang }}
<span class="time-desc">
{{if gt .Issue.Poster.Id 0}}
{{$.i18n.Tr "repo.issues.opened_by" $createdStr .Issue.Poster.HomeLink .Issue.Poster.Name | Safe}}
{{else}}
{{$.i18n.Tr "repo.issues.opened_by_fake" $createdStr .Issue.Poster.Name | Safe}}
{{end}}
·
{{$.i18n.Tr "repo.issues.num_comments" .Issue.NumComments}}
</span>
<div class="ui divider"></div>
</div>
+3
View File
@@ -46,6 +46,9 @@
</div>
</div>
{{template "repo/issue/new_form" .}}
{{template "repo/commits_table" .}}
{{template "repo/diff_box" .}}
</div>
</div>