work on PR conversation
This commit is contained in:
		
							parent
							
								
									63fecac537
								
							
						
					
					
						commit
						8c046073a8
					
				| @ -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) | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| } | ||||
| 
 | ||||
| // _________                                       __
 | ||||
| // \_   ___ \  ____   _____   _____   ____   _____/  |_
 | ||||
| // /    \  \/ /  _ \ /     \ /     \_/ __ \ /    \   __\
 | ||||
|  | ||||
							
								
								
									
										190
									
								
								models/issue.go
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								models/issue.go
									
									
									
									
									
								
							| @ -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 | ||||
| } | ||||
| 
 | ||||
| // .____          ___.          .__
 | ||||
| // |    |   _____ \_ |__   ____ |  |
 | ||||
| // |    |   \__  \ | __ \_/ __ \|  |
 | ||||
|  | ||||
| @ -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), | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										86
									
								
								modules/git/repo_pull.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								modules/git/repo_pull.go
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										2
									
								
								public/css/gogs.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/gogs.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -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; | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -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)) | ||||
| } | ||||
|  | ||||
| @ -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=""/>  {{.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}} | ||||
|  | ||||
| @ -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"> </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
									
								
								templates/repo/diff_box.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								templates/repo/diff_box.tmpl
									
									
									
									
									
										Normal 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"> </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}} | ||||
| @ -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" .}} | ||||
| @ -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
									
								
								templates/repo/issue/view_title.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								templates/repo/issue/view_title.tmpl
									
									
									
									
									
										Normal 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> | ||||
| @ -46,6 +46,9 @@ | ||||
| 			 	</div> | ||||
| 			</div> | ||||
| 			{{template "repo/issue/new_form" .}} | ||||
| 
 | ||||
| 	    {{template "repo/commits_table" .}} | ||||
| 	    {{template "repo/diff_box" .}} | ||||
| 		</div> | ||||
| 
 | ||||
| 	</div> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user