summaryrefslogtreecommitdiffstats
path: root/services/gitdiff/gitdiff.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/gitdiff/gitdiff.go')
-rw-r--r--services/gitdiff/gitdiff.go180
1 files changed, 163 insertions, 17 deletions
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 9c2aef5c97..69c8f5603a 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -13,6 +13,7 @@ import (
"html/template"
"io"
"io/ioutil"
+ "net/url"
"os"
"os/exec"
"regexp"
@@ -56,15 +57,42 @@ const (
DiffFileRename
)
+// DiffLineExpandDirection represents the DiffLineSection expand direction
+type DiffLineExpandDirection uint8
+
+// DiffLineExpandDirection possible values.
+const (
+ DiffLineExpandNone DiffLineExpandDirection = iota + 1
+ DiffLineExpandSingle
+ DiffLineExpandUpDown
+ DiffLineExpandUp
+ DiffLineExpandDown
+)
+
// DiffLine represents a line difference in a DiffSection.
type DiffLine struct {
- LeftIdx int
- RightIdx int
- Type DiffLineType
- Content string
- Comments []*models.Comment
+ LeftIdx int
+ RightIdx int
+ Type DiffLineType
+ Content string
+ Comments []*models.Comment
+ SectionInfo *DiffLineSectionInfo
+}
+
+// DiffLineSectionInfo represents diff line section meta data
+type DiffLineSectionInfo struct {
+ Path string
+ LastLeftIdx int
+ LastRightIdx int
+ LeftIdx int
+ RightIdx int
+ LeftHunkSize int
+ RightHunkSize int
}
+// BlobExceprtChunkSize represent max lines of excerpt
+const BlobExceprtChunkSize = 20
+
// GetType returns the type of a DiffLine.
func (d *DiffLine) GetType() int {
return int(d.Type)
@@ -91,6 +119,71 @@ func (d *DiffLine) GetLineTypeMarker() string {
return ""
}
+// GetBlobExcerptQuery builds query string to get blob excerpt
+func (d *DiffLine) GetBlobExcerptQuery() string {
+ query := fmt.Sprintf(
+ "last_left=%d&last_right=%d&"+
+ "left=%d&right=%d&"+
+ "left_hunk_size=%d&right_hunk_size=%d&"+
+ "path=%s",
+ d.SectionInfo.LastLeftIdx, d.SectionInfo.LastRightIdx,
+ d.SectionInfo.LeftIdx, d.SectionInfo.RightIdx,
+ d.SectionInfo.LeftHunkSize, d.SectionInfo.RightHunkSize,
+ url.QueryEscape(d.SectionInfo.Path))
+ return query
+}
+
+// GetExpandDirection gets DiffLineExpandDirection
+func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection {
+ if d.Type != DiffLineSection || d.SectionInfo == nil || d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx <= 1 {
+ return DiffLineExpandNone
+ }
+ if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 {
+ return DiffLineExpandUp
+ } else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExceprtChunkSize && d.SectionInfo.RightHunkSize > 0 {
+ return DiffLineExpandUpDown
+ } else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 {
+ return DiffLineExpandDown
+ }
+ return DiffLineExpandSingle
+}
+
+func getDiffLineSectionInfo(curFile *DiffFile, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo {
+ var (
+ leftLine int
+ leftHunk int
+ rightLine int
+ righHunk int
+ )
+ ss := strings.Split(line, "@@")
+ ranges := strings.Split(ss[1][1:], " ")
+ leftRange := strings.Split(ranges[0], ",")
+ leftLine, _ = com.StrTo(leftRange[0][1:]).Int()
+ if len(leftRange) > 1 {
+ leftHunk, _ = com.StrTo(leftRange[1]).Int()
+ }
+ if len(ranges) > 1 {
+ rightRange := strings.Split(ranges[1], ",")
+ rightLine, _ = com.StrTo(rightRange[0]).Int()
+ if len(rightRange) > 1 {
+ righHunk, _ = com.StrTo(rightRange[1]).Int()
+ }
+ } else {
+ log.Warn("Parse line number failed: %v", line)
+ rightLine = leftLine
+ righHunk = leftHunk
+ }
+ return &DiffLineSectionInfo{
+ Path: curFile.Name,
+ LastLeftIdx: lastLeftIdx,
+ LastRightIdx: lastRightIdx,
+ LeftIdx: leftLine,
+ RightIdx: rightLine,
+ LeftHunkSize: leftHunk,
+ RightHunkSize: righHunk,
+ }
+}
+
// escape a line's content or return <br> needed for copy/paste purposes
func getLineContent(content string) string {
if len(content) > 0 {
@@ -248,6 +341,53 @@ func (diffFile *DiffFile) GetHighlightClass() string {
return highlight.FileNameToHighlightClass(diffFile.Name)
}
+// GetTailSection creates a fake DiffLineSection if the last section is not the end of the file
+func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommitID, rightCommitID string) *DiffSection {
+ if diffFile.Type != DiffFileChange || diffFile.IsBin || diffFile.IsLFSFile {
+ return nil
+ }
+ leftCommit, err := gitRepo.GetCommit(leftCommitID)
+ if err != nil {
+ return nil
+ }
+ rightCommit, err := gitRepo.GetCommit(rightCommitID)
+ if err != nil {
+ return nil
+ }
+ lastSection := diffFile.Sections[len(diffFile.Sections)-1]
+ lastLine := lastSection.Lines[len(lastSection.Lines)-1]
+ leftLineCount := getCommitFileLineCount(leftCommit, diffFile.Name)
+ rightLineCount := getCommitFileLineCount(rightCommit, diffFile.Name)
+ if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx {
+ return nil
+ }
+ tailDiffLine := &DiffLine{
+ Type: DiffLineSection,
+ Content: " ",
+ SectionInfo: &DiffLineSectionInfo{
+ Path: diffFile.Name,
+ LastLeftIdx: lastLine.LeftIdx,
+ LastRightIdx: lastLine.RightIdx,
+ LeftIdx: leftLineCount,
+ RightIdx: rightLineCount,
+ }}
+ tailSection := &DiffSection{Lines: []*DiffLine{tailDiffLine}}
+ return tailSection
+
+}
+
+func getCommitFileLineCount(commit *git.Commit, filePath string) int {
+ blob, err := commit.GetBlobByPath(filePath)
+ if err != nil {
+ return 0
+ }
+ lineCount, err := blob.GetBlobLineCount()
+ if err != nil {
+ return 0
+ }
+ return lineCount
+}
+
// Diff represents a difference between two git trees.
type Diff struct {
TotalAddition, TotalDeletion int
@@ -510,19 +650,16 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
case line[0] == '@':
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
- ss := strings.Split(line, "@@")
- diffLine := &DiffLine{Type: DiffLineSection, Content: line}
- curSection.Lines = append(curSection.Lines, diffLine)
-
- // Parse line number.
- ranges := strings.Split(ss[1][1:], " ")
- leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int()
- if len(ranges) > 1 {
- rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int()
- } else {
- log.Warn("Parse line number failed: %v", line)
- rightLine = leftLine
+ lineSectionInfo := getDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1)
+ diffLine := &DiffLine{
+ Type: DiffLineSection,
+ Content: line,
+ SectionInfo: lineSectionInfo,
}
+ curSection.Lines = append(curSection.Lines, diffLine)
+ // update line number.
+ leftLine = lineSectionInfo.LeftIdx
+ rightLine = lineSectionInfo.RightIdx
continue
case line[0] == '+':
curFile.Addition++
@@ -599,6 +736,8 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
break
}
curFileLinesCount = 0
+ leftLine = 1
+ rightLine = 1
curFileLFSPrefix = false
// Check file diff type and is submodule.
@@ -701,6 +840,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
diffArgs = append(diffArgs, actualBeforeCommitID)
diffArgs = append(diffArgs, afterCommitID)
cmd = exec.Command(git.GitExecutable, diffArgs...)
+ beforeCommitID = actualBeforeCommitID
}
cmd.Dir = repoPath
cmd.Stderr = os.Stderr
@@ -721,6 +861,12 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
if err != nil {
return nil, fmt.Errorf("ParsePatch: %v", err)
}
+ for _, diffFile := range diff.Files {
+ tailSection := diffFile.GetTailSection(gitRepo, beforeCommitID, afterCommitID)
+ if tailSection != nil {
+ diffFile.Sections = append(diffFile.Sections, tailSection)
+ }
+ }
if err = cmd.Wait(); err != nil {
return nil, fmt.Errorf("Wait: %v", err)