in base repo is consistent with the head commit of head branch in the head repo
+ // get head commit of PR
+ prHeadRef := pull.GetGitRefName()
+ if err := pull.LoadBaseRepo(ctx); err != nil {
+ ctx.ServerError("Unable to load base repo", err)
+ return
+ }
+ prHeadCommitID, err := git.GetFullCommitID(ctx, pull.BaseRepo.RepoPath(), prHeadRef)
+ if err != nil {
+ ctx.ServerError("Get head commit Id of pr fail", err)
+ return
+ }
+
+ // get head commit of branch in the head repo
+ if err := pull.LoadHeadRepo(ctx); err != nil {
+ ctx.ServerError("Unable to load head repo", err)
+ return
+ }
+ if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
+ // todo localize
+ ctx.JSONError("The origin branch is delete, cannot reopen.")
+ return
+ }
+ headBranchRef := pull.GetGitHeadBranchRefName()
+ headBranchCommitID, err := git.GetFullCommitID(ctx, pull.HeadRepo.RepoPath(), headBranchRef)
+ if err != nil {
+ ctx.ServerError("Get head commit Id of head branch fail", err)
+ return
+ }
+
+ err = pull.LoadIssue(ctx)
+ if err != nil {
+ ctx.ServerError("load the issue of pull request error", err)
+ return
+ }
+
+ if prHeadCommitID != headBranchCommitID {
+ // force push to base repo
+ err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{
+ Remote: pull.BaseRepo.RepoPath(),
+ Branch: pull.HeadBranch + ":" + prHeadRef,
+ Force: true,
+ Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo),
+ })
+ if err != nil {
+ ctx.ServerError("force push error", err)
+ return
+ }
+ }
+ }
+
+ if pr != nil {
+ ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index))
+ } else {
+ issue.IsClosed = form.Status == "close"
+ issue.ClosedStatus = issues_model.IssueClosedStatus(0)
+ if issue.IsClosed {
+ issue.ClosedStatus = form.ClosedStatus
+ }
+ if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, ""); err != nil {
+ log.Error("ChangeStatus: %v", err)
+
+ if issues_model.IsErrDependenciesLeft(err) {
+ if issue.IsPull {
+ ctx.JSONError(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
+ } else {
+ ctx.JSONError(ctx.Tr("repo.issues.dependency.issue_close_blocked"))
+ }
+ return
+ }
+ } else {
+ if err := stopTimerIfAvailable(ctx.Doer, issue); err != nil {
+ ctx.ServerError("CreateOrStopIssueStopwatch", err)
+ return
+ }
+
+ log.Trace("Issue [%d] status changed to closed: %v", issue.ID, issue.IsClosed)
+ }
+ }
+
+ }
+
+ // Redirect to comment hashtag if there is any actual content.
+ typeName := "issues"
+ if issue.IsPull {
+ typeName = "pulls"
+ }
+ if comment != nil {
+ ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d#%s", ctx.Repo.RepoLink, typeName, issue.Index, comment.HashTag()))
+ } else {
+ ctx.JSONRedirect(fmt.Sprintf("%s/%s/%d", ctx.Repo.RepoLink, typeName, issue.Index))
+ }
+}
+
// UpdateCommentContent change comment of issue's content
func UpdateCommentContent(ctx *context.Context) {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64(":id"))
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index 988e479a48138..5eceab25eac2c 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -463,9 +463,10 @@ func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) bindi
// CreateCommentForm form for creating comment
type CreateCommentForm struct {
- Content string
- Status string `binding:"OmitEmpty;In(reopen,close)"`
- Files []string
+ Content string
+ Status string `binding:"OmitEmpty;In(reopen,close)"`
+ Files []string
+ ClosedStatus issues_model.IssueClosedStatus
}
// Validate validates the fields
diff --git a/services/issue/commit.go b/services/issue/commit.go
index 0579e0f5c53e6..ad910ce565e7d 100644
--- a/services/issue/commit.go
+++ b/services/issue/commit.go
@@ -196,6 +196,7 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m
}
if isClosed != refIssue.IsClosed {
refIssue.Repo = refRepo
+ refIssue.IsClosed = close
if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, isClosed); err != nil {
return err
}
diff --git a/services/issue/status.go b/services/issue/status.go
index 967c29bd22230..fea7535e3005b 100644
--- a/services/issue/status.go
+++ b/services/issue/status.go
@@ -19,7 +19,7 @@ import (
func ChangeStatus(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool) error {
comment, err := issues_model.ChangeIssueStatus(ctx, issue, doer, closed)
if err != nil {
- if issues_model.IsErrDependenciesLeft(err) && closed {
+ if issues_model.IsErrDependenciesLeft(err) && issue.IsClosed {
if err := issues_model.FinishIssueStopwatchIfPossible(ctx, doer, issue); err != nil {
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
}
@@ -27,7 +27,7 @@ func ChangeStatus(ctx context.Context, issue *issues_model.Issue, doer *user_mod
return err
}
- if closed {
+ if issue.IsClosed {
if err := issues_model.FinishIssueStopwatchIfPossible(ctx, doer, issue); err != nil {
return err
}
diff --git a/services/pull/merge.go b/services/pull/merge.go
index a3fbe4f627b00..b77dc2e70f8c2 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -242,8 +242,9 @@ func handleCloseCrossReferences(ctx context.Context, pr *issues_model.PullReques
if err = ref.Issue.LoadRepo(ctx); err != nil {
return err
}
- isClosed := ref.RefAction == references.XRefActionCloses
- if isClosed != ref.Issue.IsClosed {
+ close := ref.RefAction == references.XRefActionCloses
+ if close != ref.Issue.IsClosed {
+ ref.Issue.IsClosed = close
if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, isClosed); err != nil {
// Allow ErrDependenciesLeft
if !issues_model.IsErrDependenciesLeft(err) {
diff --git a/services/pull/pull.go b/services/pull/pull.go
index bab4e49998e15..27aef100f3f8d 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -662,7 +662,8 @@ func CloseBranchPulls(ctx context.Context, doer *user_model.User, repoID int64,
var errs errlist
for _, pr := range prs {
- if err = issue_service.ChangeStatus(ctx, pr.Issue, doer, "", true); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) {
+ pr.Issue.IsClosed = true
+ if err = issue_service.ChangeStatus(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) {
errs = append(errs, err)
}
}
@@ -696,7 +697,8 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
if pr.BaseRepoID == repo.ID {
continue
}
- if err = issue_service.ChangeStatus(ctx, pr.Issue, doer, "", true); err != nil && !issues_model.IsErrPullWasClosed(err) {
+ pr.Issue.IsClosed = true
+ if err = issue_service.ChangeStatus(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrPullWasClosed(err) {
errs = append(errs, err)
}
}
diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl
index 395b118d3ef22..6f1624779c185 100644
--- a/templates/mail/issue/default.tmpl
+++ b/templates/mail/issue/default.tmpl
@@ -36,7 +36,15 @@
{{end}}
{{if eq .ActionName "close"}}
- {{.locale.Tr "mail.issue.action.close" .Doer.Name .Issue.Index}}
+ {{$closeTrans := "mail.issue.action.close"}}
+ {{if eq .Issue.ClosedStatus 1}}
+ {{$closeTrans = "mail.issue.action.close_as_archived"}}
+ {{else if eq .Issue.ClosedStatus 2}}
+ {{$closeTrans = "mail.issue.action.close_as_resolved"}}
+ {{else if eq .Issue.ClosedStatus 3}}
+ {{$closeTrans = "mail.issue.action.close_as_stale"}}
+ {{end}}
+ {{.locale.Tr $closeTrans (Escape .Doer.Name) .Issue.Index | Str2html}}
{{else if eq .ActionName "reopen"}}
{{.locale.Tr "mail.issue.action.reopen" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "merge"}}
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index e4213b8fcd6b3..ab00a798e4c40 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -92,20 +92,41 @@