diff --git a/docs/content/doc/features/comparison.en-us.md b/docs/content/doc/features/comparison.en-us.md
index 2a622a4b39c4..77299404cb65 100644
--- a/docs/content/doc/features/comparison.en-us.md
+++ b/docs/content/doc/features/comparison.en-us.md
@@ -47,7 +47,7 @@ _Symbols used in table:_
| Built-in Container Registry | [✘](https://github.com/go-gitea/gitea/issues/2316) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| External git mirroring | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
| FIDO U2F (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
-| Built-in CI/CD | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
+| Built-in CI/CD | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Subgroups: groups within groups | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
#### Code management
diff --git a/docs/content/doc/installation/on-kubernetes.en-us.md b/docs/content/doc/installation/on-kubernetes.en-us.md
new file mode 100644
index 000000000000..025117f64a72
--- /dev/null
+++ b/docs/content/doc/installation/on-kubernetes.en-us.md
@@ -0,0 +1,27 @@
+---
+date: "2020-03-19T19:27:00+02:00"
+title: "Install on Kubernetes"
+slug: "install-on-kubernetes"
+weight: 10
+toc: true
+draft: false
+menu:
+ sidebar:
+ parent: "installation"
+ name: "Kubernetes"
+ weight: 50
+ identifier: "install-on-kubernetes"
+---
+
+# Installation with Helm (on Kubernetes)
+
+Gitea provides a Helm Chart to allow for installation on kubernetes.
+
+A non-customized install can be done with:
+
+```
+helm repo add gitea-charts https://dl.gitea.io/charts/
+helm install gitea gitea-charts/gitea
+```
+
+If you would like to customize your install, which includes kubernetes ingress, please refer to the complete [Gitea helm chart configuration details](https://gitea.com/gitea/helm-chart/)
diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go
index b0031ef33299..b8e513958e84 100644
--- a/integrations/api_helper_for_declarative_test.go
+++ b/integrations/api_helper_for_declarative_test.go
@@ -235,6 +235,10 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64)
DecodeJSON(t, resp, &err)
assert.EqualValues(t, "Please try again later", err.Message)
queue.GetManager().FlushAll(context.Background(), 5*time.Second)
+ req = NewRequestWithJSON(t, http.MethodPost, urlStr, &auth.MergePullRequestForm{
+ MergeMessageField: "doAPIMergePullRequest Merge",
+ Do: string(models.MergeStyleMerge),
+ })
resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus)
}
diff --git a/integrations/repo_branch_test.go b/integrations/repo_branch_test.go
index 68806f347bd3..de4e66898797 100644
--- a/integrations/repo_branch_test.go
+++ b/integrations/repo_branch_test.go
@@ -104,13 +104,12 @@ func testCreateBranches(t *testing.T, giteaURL *url.URL) {
{
OldRefSubURL: "tag/v1.0.0",
NewBranch: "feature/test4",
- CreateRelease: "v1.0.0",
+ CreateRelease: "v1.0.1",
ExpectedStatus: http.StatusFound,
FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test4"),
},
}
for _, test := range tests {
- defer prepareTestEnv(t)()
session := loginUser(t, "user2")
if test.CreateRelease != "" {
createNewRelease(t, session, "/user2/repo1", test.CreateRelease, test.CreateRelease, false, false)
diff --git a/integrations/repo_commits_search_test.go b/integrations/repo_commits_search_test.go
index 3cd548e81029..0dc0588297f1 100644
--- a/integrations/repo_commits_search_test.go
+++ b/integrations/repo_commits_search_test.go
@@ -14,8 +14,6 @@ import (
)
func testRepoCommitsSearch(t *testing.T, query, commit string) {
- defer prepareTestEnv(t)()
-
session := loginUser(t, "user2")
// Request repository commits page
@@ -28,6 +26,7 @@ func testRepoCommitsSearch(t *testing.T, query, commit string) {
}
func TestRepoCommitsSearch(t *testing.T) {
+ defer prepareTestEnv(t)()
testRepoCommitsSearch(t, "e8eabd", "")
testRepoCommitsSearch(t, "38a9cb", "")
testRepoCommitsSearch(t, "6e8e", "6e8eabd9a7")
diff --git a/models/issue_comment.go b/models/issue_comment.go
index a7e9c049bf30..7bcea40b9300 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -712,6 +712,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
RefAction: opts.RefAction,
RefIsPull: opts.RefIsPull,
IsForcePush: opts.IsForcePush,
+ Invalidated: opts.Invalidated,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
@@ -878,6 +879,7 @@ type CreateCommentOptions struct {
RefAction references.XRefAction
RefIsPull bool
IsForcePush bool
+ Invalidated bool
}
// CreateComment creates comment of issue or commit.
@@ -953,6 +955,8 @@ type FindCommentsOptions struct {
ReviewID int64
Since int64
Before int64
+ Line int64
+ TreePath string
Type CommentType
}
@@ -976,6 +980,12 @@ func (opts *FindCommentsOptions) toConds() builder.Cond {
if opts.Type != CommentTypeUnknown {
cond = cond.And(builder.Eq{"comment.type": opts.Type})
}
+ if opts.Line > 0 {
+ cond = cond.And(builder.Eq{"comment.line": opts.Line})
+ }
+ if len(opts.TreePath) > 0 {
+ cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath})
+ }
return cond
}
@@ -990,6 +1000,8 @@ func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) {
sess = opts.setSessionPagination(sess)
}
+ // WARNING: If you change this order you will need to fix createCodeComment
+
return comments, sess.
Asc("comment.created_unix").
Asc("comment.id").
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index cbf8ae87327e..4715f192c157 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -250,6 +250,8 @@ var migrations = []Migration{
NewMigration("fix publisher ID for tag releases", fixPublisherIDforTagReleases),
// v157 -> v158
NewMigration("ensure repo topics are up-to-date", fixRepoTopics),
+ // v158 -> v159
+ NewMigration("code comment replies should have the commitID of the review they are replying to", updateCodeCommentReplies),
}
// GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v158.go b/models/migrations/v158.go
new file mode 100644
index 000000000000..a6f2178f8744
--- /dev/null
+++ b/models/migrations/v158.go
@@ -0,0 +1,110 @@
+// Copyright 2020 The Gitea 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 migrations
+
+import (
+ "fmt"
+ "strconv"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "xorm.io/xorm"
+)
+
+func updateCodeCommentReplies(x *xorm.Engine) error {
+ type Comment struct {
+ ID int64 `xorm:"pk autoincr"`
+ CommitSHA string `xorm:"VARCHAR(40)"`
+ Patch string `xorm:"TEXT patch"`
+ Invalidated bool
+
+ // Not extracted but used in the below query
+ Type int `xorm:"INDEX"`
+ Line int64 // - previous line / + proposed line
+ TreePath string
+ ReviewID int64 `xorm:"index"`
+ }
+
+ if err := x.Sync2(new(Comment)); err != nil {
+ return err
+ }
+
+ sqlSelect := `SELECT comment.id as id, first.commit_sha as commit_sha, first.patch as patch, first.invalidated as invalidated`
+ sqlTail := ` FROM comment INNER JOIN (
+ SELECT C.id, C.review_id, C.line, C.tree_path, C.patch, C.commit_sha, C.invalidated
+ FROM comment AS C
+ WHERE C.type = 21
+ AND C.created_unix =
+ (SELECT MIN(comment.created_unix)
+ FROM comment
+ WHERE comment.review_id = C.review_id
+ AND comment.type = 21
+ AND comment.line = C.line
+ AND comment.tree_path = C.tree_path)
+ ) AS first
+ ON comment.review_id = first.review_id
+ AND comment.tree_path = first.tree_path AND comment.line = first.line
+ WHERE comment.type = 21
+ AND comment.id != first.id
+ AND comment.commit_sha != first.commit_sha`
+
+ var sqlCmd string
+ var start = 0
+ var batchSize = 100
+ sess := x.NewSession()
+ defer sess.Close()
+ for {
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ if setting.Database.UseMSSQL {
+ if _, err := sess.Exec(sqlSelect + " INTO #temp_comments" + sqlTail); err != nil {
+ log.Error("unable to create temporary table")
+ return err
+ }
+ }
+
+ var comments = make([]*Comment, 0, batchSize)
+
+ switch {
+ case setting.Database.UseMySQL:
+ sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + ", " + strconv.Itoa(start)
+ case setting.Database.UsePostgreSQL:
+ fallthrough
+ case setting.Database.UseSQLite3:
+ sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + " OFFSET " + strconv.Itoa(start)
+ case setting.Database.UseMSSQL:
+ sqlCmd = "SELECT TOP " + strconv.Itoa(batchSize) + " * FROM #temp_comments WHERE " +
+ "(id NOT IN ( SELECT TOP " + strconv.Itoa(start) + " id FROM #temp_comments ORDER BY id )) ORDER BY id"
+ default:
+ return fmt.Errorf("Unsupported database type")
+ }
+
+ if err := sess.SQL(sqlCmd).Find(&comments); err != nil {
+ log.Error("failed to select: %v", err)
+ return err
+ }
+
+ for _, comment := range comments {
+ if _, err := sess.Table("comment").ID(comment.ID).Cols("commit_sha", "patch", "invalidated").Update(comment); err != nil {
+ log.Error("failed to update comment[%d]: %v %v", comment.ID, comment, err)
+ return err
+ }
+ }
+
+ start += len(comments)
+
+ if err := sess.Commit(); err != nil {
+ return err
+ }
+ if len(comments) < batchSize {
+ break
+ }
+ }
+
+ return nil
+}
diff --git a/modules/references/references.go b/modules/references/references.go
index 070c6e566a8e..6e0baefc6e1b 100644
--- a/modules/references/references.go
+++ b/modules/references/references.go
@@ -235,40 +235,78 @@ func findAllIssueReferencesMarkdown(content string) []*rawReference {
return findAllIssueReferencesBytes(bcontent, links)
}
+func convertFullHTMLReferencesToShortRefs(re *regexp.Regexp, contentBytes *[]byte) {
+ // We will iterate through the content, rewrite and simplify full references.
+ //
+ // We want to transform something like:
+ //
+ // this is a https://ourgitea.com/git/owner/repo/issues/123456789, foo
+ // https://ourgitea.com/git/owner/repo/pulls/123456789
+ //
+ // Into something like:
+ //
+ // this is a #123456789, foo
+ // !123456789
+
+ pos := 0
+ for {
+ // re looks for something like: (\s|^|\(|\[)https://ourgitea.com/git/(owner/repo)/(issues)/(123456789)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)
+ match := re.FindSubmatchIndex((*contentBytes)[pos:])
+ if match == nil {
+ break
+ }
+ // match is a bunch of indices into the content from pos onwards so
+ // to simplify things let's just add pos to all of the indices in match
+ for i := range match {
+ match[i] += pos
+ }
+
+ // match[0]-match[1] is whole string
+ // match[2]-match[3] is preamble
+
+ // move the position to the end of the preamble
+ pos = match[3]
+
+ // match[4]-match[5] is owner/repo
+ // now copy the owner/repo to end of the preamble
+ endPos := pos + match[5] - match[4]
+ copy((*contentBytes)[pos:endPos], (*contentBytes)[match[4]:match[5]])
+
+ // move the current position to the end of the newly copied owner/repo
+ pos = endPos
+
+ // Now set the issue/pull marker:
+ //
+ // match[6]-match[7] == 'issues'
+ (*contentBytes)[pos] = '#'
+ if string((*contentBytes)[match[6]:match[7]]) == "pulls" {
+ (*contentBytes)[pos] = '!'
+ }
+ pos++
+
+ // Then add the issue/pull number
+ //
+ // match[8]-match[9] is the number
+ endPos = pos + match[9] - match[8]
+ copy((*contentBytes)[pos:endPos], (*contentBytes)[match[8]:match[9]])
+
+ // Now copy what's left at the end of the string to the new end position
+ copy((*contentBytes)[endPos:], (*contentBytes)[match[9]:])
+ // now we reset the length
+
+ // our new section has length endPos - match[3]
+ // our old section has length match[9] - match[3]
+ (*contentBytes) = (*contentBytes)[:len((*contentBytes))-match[9]+endPos]
+ pos = endPos
+ }
+}
+
// FindAllIssueReferences returns a list of unvalidated references found in a string.
func FindAllIssueReferences(content string) []IssueReference {
// Need to convert fully qualified html references to local system to #/! short codes
contentBytes := []byte(content)
if re := getGiteaIssuePullPattern(); re != nil {
- pos := 0
- for {
- match := re.FindSubmatchIndex(contentBytes[pos:])
- if match == nil {
- break
- }
- // match[0]-match[1] is whole string
- // match[2]-match[3] is preamble
- pos += match[3]
- // match[4]-match[5] is owner/repo
- endPos := pos + match[5] - match[4]
- copy(contentBytes[pos:endPos], contentBytes[match[4]:match[5]])
- pos = endPos
- // match[6]-match[7] == 'issues'
- contentBytes[pos] = '#'
- if string(contentBytes[match[6]:match[7]]) == "pulls" {
- contentBytes[pos] = '!'
- }
- pos++
- // match[8]-match[9] is the number
- endPos = pos + match[9] - match[8]
- copy(contentBytes[pos:endPos], contentBytes[match[8]:match[9]])
- copy(contentBytes[endPos:], contentBytes[match[9]:])
- // now we reset the length
- // our new section has length endPos - match[3]
- // our old section has length match[9] - match[3]
- contentBytes = contentBytes[:len(contentBytes)-match[9]+endPos]
- pos = endPos
- }
+ convertFullHTMLReferencesToShortRefs(re, &contentBytes)
} else {
log.Debug("No GiteaIssuePullPattern pattern")
}
diff --git a/modules/references/references_test.go b/modules/references/references_test.go
index 0c4037f1204a..f51379e4c332 100644
--- a/modules/references/references_test.go
+++ b/modules/references/references_test.go
@@ -5,6 +5,7 @@
package references
import (
+ "regexp"
"testing"
"code.gitea.io/gitea/modules/setting"
@@ -29,6 +30,26 @@ type testResult struct {
TimeLog string
}
+func TestConvertFullHTMLReferencesToShortRefs(t *testing.T) {
+ re := regexp.MustCompile(`(\s|^|\(|\[)` +
+ regexp.QuoteMeta("https://ourgitea.com/git/") +
+ `([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+)/` +
+ `((?:issues)|(?:pulls))/([0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
+ test := `this is a https://ourgitea.com/git/owner/repo/issues/123456789, foo
+https://ourgitea.com/git/owner/repo/pulls/123456789
+ And https://ourgitea.com/git/owner/repo/pulls/123
+`
+ expect := `this is a owner/repo#123456789, foo
+owner/repo!123456789
+ And owner/repo!123
+`
+
+ contentBytes := []byte(test)
+ convertFullHTMLReferencesToShortRefs(re, &contentBytes)
+ result := string(contentBytes)
+ assert.EqualValues(t, expect, result)
+}
+
func TestFindAllIssueReferences(t *testing.T) {
fixtures := []testFixture{
@@ -106,6 +127,13 @@ func TestFindAllIssueReferences(t *testing.T) {
{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
},
},
+ {
+ "This http://gitea.com:3000/user4/repo5/pulls/202 yes. http://gitea.com:3000/user4/repo5/pulls/203 no",
+ []testResult{
+ {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
+ {203, "user4", "repo5", "203", true, XRefActionNone, nil, nil, ""},
+ },
+ },
{
"This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
[]testResult{
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index fb5a9d4d7068..cbdaf686d732 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -796,7 +796,7 @@ tree=Ağaç
clear_ref='Geçerli referansı temizle'
filter_branch_and_tag=Dal veya biçim imini filtrele
branches=Dal
-tags=Biçim İmleri
+tags=Etiket
issues=Konular
pulls=Değişiklik İstekleri
project_board=Projeler
@@ -1055,7 +1055,7 @@ issues.context.edit=Düzenle
issues.context.delete=Sil
issues.no_content=Henüz bir içerik yok.
issues.close_issue=Kapat
-issues.pull_merged_at=`%[2]s işlemesini %[3]s %[4]s ile birleştirdi`
+issues.pull_merged_at=`%[2]s işlemesini %[3]s ile %[4]s birleştirdi`
issues.close_comment_issue=Yorum Yap ve Kapat
issues.reopen_issue=Yeniden aç
issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç
@@ -1144,8 +1144,8 @@ issues.due_date=Bitiş Tarihi
issues.invalid_due_date_format=Bitiş tarihinin biçimi 'yyyy-aa-gg' olmalıdır.
issues.error_modifying_due_date=Bitiş tarihi değiştirilemedi.
issues.error_removing_due_date=Bitiş tarihi silinemedi.
-issues.push_commit_1=%d %s işlemesi eklendi
-issues.push_commits_n=%d %s işlemesi eklendi
+issues.push_commit_1=%d işlemeyi %s ekledi
+issues.push_commits_n=%d işlemeyi %s ekledi
issues.force_push_codes=`%[1]s %[2]s konumundan %[4]s %[6]s konumuna zorla güncellendi`
issues.due_date_form=yyyy-aa-gg
issues.due_date_form_add=Bitiş tarihi ekle
@@ -1221,7 +1221,7 @@ pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gere
pulls.has_pull_request=`Bu dallar arasında bir değişiklik isteği zaten var: %[2]s#%[3]d`
pulls.create=Değişiklik İsteği Oluştur
pulls.title_desc=%[2]s
içindeki %[1]d işlemeyi %[3]s
ile birleştirmek istiyor
-pulls.merged_title_desc=%[4]s %[2]s
içindeki %[1]d işleme %[3]s
ile birleştirdi
+pulls.merged_title_desc=%[4]s %[2]s
içindeki %[1]d işlemeyi %[3]s
ile birleştirdi
pulls.change_target_branch_at='hedef dal %s adresinden %s%s adresine değiştirildi'
pulls.tab_conversation=Sohbet
pulls.tab_commits=İşleme
@@ -1263,9 +1263,9 @@ pulls.no_merge_wip=Bu değişiklik isteği birleştirilemez çünkü devam eden
pulls.no_merge_not_ready=Bu değişiklik isteği birleştirilmeye hazır değil, inceleme durumunu ve durum kontrollerini kontrol edin.
pulls.no_merge_access=Bu değişiklik isteğini birleştirme yetkiniz yok.
pulls.merge_pull_request=Değişiklik İsteğini Birleştir
-pulls.rebase_merge_pull_request=Rebase ve Merge
-pulls.rebase_merge_commit_pull_request=Rebase ve Merge (--no-ff)
-pulls.squash_merge_pull_request=Squash ve Merge
+pulls.rebase_merge_pull_request=Yeniden Yapılandır ve Birleştir
+pulls.rebase_merge_commit_pull_request=Yeniden Yapılandır ve Birleştir (--no-ff)
+pulls.squash_merge_pull_request=Ez ve Birleştir
pulls.require_signed_wont_sign=Dal imzalı işlemeler gerektiriyor, ancak bu birleştirme imzalanmayacak
pulls.invalid_merge_option=Bu değişiklik isteği için bu birleştirme seçeneğini kullanamazsınız.
pulls.merge_conflict=Birleştirme Başarısız Oldu: Birleştirme sırasında bir çakışma oldu. İpucu: Farklı bir strateji deneyin
@@ -1482,7 +1482,7 @@ settings.pulls.ignore_whitespace=Çakışmalar için Boşlukları Gözardı Et
settings.pulls.allow_merge_commits=İşleme Birleştirmeyi Etkinleştir
settings.pulls.allow_rebase_merge=İşlemeleri Birleştirmek için Yeniden Yapılandırmayı Etkinleştir
settings.pulls.allow_rebase_merge_commit=Açık birleştirme işlemeleri ile Yeniden Yapılandırmayı Etkinleştir (--no-ff)
-settings.pulls.allow_squash_commits=İşlemeleri Birleştirmek için Sıkmayı Etkinleştir
+settings.pulls.allow_squash_commits=İşlemeleri Birleştirmek için Ezmeyi Etkinleştir
settings.projects_desc=Depo Projelerini Etkinleştir
settings.admin_settings=Yönetici Ayarları
settings.admin_enable_health_check=Depo Sağlık Kontrollerini Etkinleştir (git fsck)
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index f96c2719df02..8d43e0a8cf92 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -820,6 +820,8 @@ audio_not_supported_in_browser=您的瀏覽器不支援 HTML5 'audio' 標籤
stored_lfs=已使用 Git LFS 儲存
symbolic_link=符號連結
commit_graph=提交線圖
+commit_graph.select=選擇分支
+commit_graph.hide_pr_refs=隱藏合併請求
commit_graph.monochrome=單色
commit_graph.color=彩色
blame=Blame
diff --git a/services/pull/review.go b/services/pull/review.go
index 99afdd73c215..f0ee234a4241 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -122,41 +122,76 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models
}
defer gitRepo.Close()
- // FIXME validate treePath
- // Get latest commit referencing the commented line
- // No need for get commit for base branch changes
+ invalidated := false
+ head := pr.GetGitRefName()
if line > 0 {
- commit, err := gitRepo.LineBlame(pr.GetGitRefName(), gitRepo.Path, treePath, uint(line))
- if err == nil {
- commitID = commit.ID.String()
- } else if !(strings.Contains(err.Error(), "exit status 128 - fatal: no such path") || notEnoughLines.MatchString(err.Error())) {
- return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %v", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
+ if reviewID != 0 {
+ first, err := models.FindComments(models.FindCommentsOptions{
+ ReviewID: reviewID,
+ Line: line,
+ TreePath: treePath,
+ Type: models.CommentTypeCode,
+ ListOptions: models.ListOptions{
+ PageSize: 1,
+ Page: 1,
+ },
+ })
+ if err == nil && len(first) > 0 {
+ commitID = first[0].CommitSHA
+ invalidated = first[0].Invalidated
+ patch = first[0].Patch
+ } else if err != nil && !models.IsErrCommentNotExist(err) {
+ return nil, fmt.Errorf("Find first comment for %d line %d path %s. Error: %v", reviewID, line, treePath, err)
+ } else {
+ review, err := models.GetReviewByID(reviewID)
+ if err == nil && len(review.CommitID) > 0 {
+ head = review.CommitID
+ } else if err != nil && !models.IsErrReviewNotExist(err) {
+ return nil, fmt.Errorf("GetReviewByID %d. Error: %v", reviewID, err)
+ }
+ }
+ }
+
+ if len(commitID) == 0 {
+ // FIXME validate treePath
+ // Get latest commit referencing the commented line
+ // No need for get commit for base branch changes
+ commit, err := gitRepo.LineBlame(head, gitRepo.Path, treePath, uint(line))
+ if err == nil {
+ commitID = commit.ID.String()
+ } else if !(strings.Contains(err.Error(), "exit status 128 - fatal: no such path") || notEnoughLines.MatchString(err.Error())) {
+ return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %v", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
+ }
}
}
// Only fetch diff if comment is review comment
- if reviewID != 0 {
- headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
- if err != nil {
- return nil, fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
+ if len(patch) == 0 && reviewID != 0 {
+ if len(commitID) == 0 {
+ commitID, err = gitRepo.GetRefCommitID(pr.GetGitRefName())
+ if err != nil {
+ return nil, fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
+ }
}
+
patchBuf := new(bytes.Buffer)
- if err := git.GetRepoRawDiffForFile(gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, treePath, patchBuf); err != nil {
- return nil, fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", err, gitRepo.Path, pr.MergeBase, headCommitID, treePath)
+ if err := git.GetRepoRawDiffForFile(gitRepo, pr.MergeBase, commitID, git.RawDiffNormal, treePath, patchBuf); err != nil {
+ return nil, fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", gitRepo.Path, pr.MergeBase, commitID, treePath, err)
}
patch = git.CutDiffAroundLine(patchBuf, int64((&models.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
}
return models.CreateComment(&models.CreateCommentOptions{
- Type: models.CommentTypeCode,
- Doer: doer,
- Repo: repo,
- Issue: issue,
- Content: content,
- LineNum: line,
- TreePath: treePath,
- CommitSHA: commitID,
- ReviewID: reviewID,
- Patch: patch,
+ Type: models.CommentTypeCode,
+ Doer: doer,
+ Repo: repo,
+ Issue: issue,
+ Content: content,
+ LineNum: line,
+ TreePath: treePath,
+ CommitSHA: commitID,
+ ReviewID: reviewID,
+ Patch: patch,
+ Invalidated: invalidated,
})
}
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index e960f7fe1751..1b102b11d883 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -37,7 +37,7 @@