|
|
@ -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) |
|
|
|