diff options
Diffstat (limited to 'services/gitdiff/gitdiff.go')
-rw-r--r-- | services/gitdiff/gitdiff.go | 180 |
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) |