diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2019-09-06 10:20:09 +0800 |
---|---|---|
committer | techknowlogick <techknowlogick@gitea.io> | 2019-09-05 22:20:09 -0400 |
commit | c03d75fbd51174d0e7ffdbaf9e9e253438d06cf7 (patch) | |
tree | a54e9fdfb6ff96baf7010d7fd5049a05a16644e2 /models | |
parent | b660a732ae283d863636ead9cc1a365ce1c0edc1 (diff) | |
download | gitea-c03d75fbd51174d0e7ffdbaf9e9e253438d06cf7.tar.gz gitea-c03d75fbd51174d0e7ffdbaf9e9e253438d06cf7.zip |
Move git diff codes from models to services/gitdiff (#7889)
* move git diff codes from models to services/gitdiff
* fix template
* fix test
* fix template
Diffstat (limited to 'models')
-rw-r--r-- | models/git_diff.go | 800 | ||||
-rw-r--r-- | models/git_diff_test.go | 190 | ||||
-rw-r--r-- | models/issue_comment.go | 81 | ||||
-rw-r--r-- | models/models.go | 5 |
4 files changed, 5 insertions, 1071 deletions
diff --git a/models/git_diff.go b/models/git_diff.go deleted file mode 100644 index b822685409..0000000000 --- a/models/git_diff.go +++ /dev/null @@ -1,800 +0,0 @@ -// Copyright 2014 The Gogs 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 models - -import ( - "bufio" - "bytes" - "fmt" - "html" - "html/template" - "io" - "io/ioutil" - "os" - "os/exec" - "regexp" - "sort" - "strconv" - "strings" - - "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/highlight" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" - - "github.com/sergi/go-diff/diffmatchpatch" - "github.com/unknwon/com" - stdcharset "golang.org/x/net/html/charset" - "golang.org/x/text/transform" -) - -// DiffLineType represents the type of a DiffLine. -type DiffLineType uint8 - -// DiffLineType possible values. -const ( - DiffLinePlain DiffLineType = iota + 1 - DiffLineAdd - DiffLineDel - DiffLineSection -) - -// DiffFileType represents the type of a DiffFile. -type DiffFileType uint8 - -// DiffFileType possible values. -const ( - DiffFileAdd DiffFileType = iota + 1 - DiffFileChange - DiffFileDel - DiffFileRename -) - -// DiffLine represents a line difference in a DiffSection. -type DiffLine struct { - LeftIdx int - RightIdx int - Type DiffLineType - Content string - Comments []*Comment -} - -// GetType returns the type of a DiffLine. -func (d *DiffLine) GetType() int { - return int(d.Type) -} - -// CanComment returns whether or not a line can get commented -func (d *DiffLine) CanComment() bool { - return len(d.Comments) == 0 && d.Type != DiffLineSection -} - -// GetCommentSide returns the comment side of the first comment, if not set returns empty string -func (d *DiffLine) GetCommentSide() string { - if len(d.Comments) == 0 { - return "" - } - return d.Comments[0].DiffSide() -} - -// GetLineTypeMarker returns the line type marker -func (d *DiffLine) GetLineTypeMarker() string { - if strings.IndexByte(" +-", d.Content[0]) > -1 { - return d.Content[0:1] - } - return "" -} - -// escape a line's content or return <br> needed for copy/paste purposes -func getLineContent(content string) string { - if len(content) > 0 { - return html.EscapeString(content) - } - return "<br>" -} - -// DiffSection represents a section of a DiffFile. -type DiffSection struct { - Name string - Lines []*DiffLine -} - -var ( - addedCodePrefix = []byte(`<span class="added-code">`) - removedCodePrefix = []byte(`<span class="removed-code">`) - codeTagSuffix = []byte(`</span>`) -) - -func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML { - buf := bytes.NewBuffer(nil) - - for i := range diffs { - switch { - case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd: - buf.Write(addedCodePrefix) - buf.WriteString(getLineContent(diffs[i].Text)) - buf.Write(codeTagSuffix) - case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel: - buf.Write(removedCodePrefix) - buf.WriteString(getLineContent(diffs[i].Text)) - buf.Write(codeTagSuffix) - case diffs[i].Type == diffmatchpatch.DiffEqual: - buf.WriteString(getLineContent(diffs[i].Text)) - } - } - - return template.HTML(buf.Bytes()) -} - -// GetLine gets a specific line by type (add or del) and file line number -func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine { - var ( - difference = 0 - addCount = 0 - delCount = 0 - matchDiffLine *DiffLine - ) - -LOOP: - for _, diffLine := range diffSection.Lines { - switch diffLine.Type { - case DiffLineAdd: - addCount++ - case DiffLineDel: - delCount++ - default: - if matchDiffLine != nil { - break LOOP - } - difference = diffLine.RightIdx - diffLine.LeftIdx - addCount = 0 - delCount = 0 - } - - switch lineType { - case DiffLineDel: - if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference { - matchDiffLine = diffLine - } - case DiffLineAdd: - if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference { - matchDiffLine = diffLine - } - } - } - - if addCount == delCount { - return matchDiffLine - } - return nil -} - -var diffMatchPatch = diffmatchpatch.New() - -func init() { - diffMatchPatch.DiffEditCost = 100 -} - -// GetComputedInlineDiffFor computes inline diff for the given line. -func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML { - if setting.Git.DisableDiffHighlight { - return template.HTML(getLineContent(diffLine.Content[1:])) - } - var ( - compareDiffLine *DiffLine - diff1 string - diff2 string - ) - - // try to find equivalent diff line. ignore, otherwise - switch diffLine.Type { - case DiffLineAdd: - compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx) - if compareDiffLine == nil { - return template.HTML(getLineContent(diffLine.Content[1:])) - } - diff1 = compareDiffLine.Content - diff2 = diffLine.Content - case DiffLineDel: - compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx) - if compareDiffLine == nil { - return template.HTML(getLineContent(diffLine.Content[1:])) - } - diff1 = diffLine.Content - diff2 = compareDiffLine.Content - default: - if strings.IndexByte(" +-", diffLine.Content[0]) > -1 { - return template.HTML(getLineContent(diffLine.Content[1:])) - } - return template.HTML(getLineContent(diffLine.Content)) - } - - diffRecord := diffMatchPatch.DiffMain(diff1[1:], diff2[1:], true) - diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord) - - return diffToHTML(diffRecord, diffLine.Type) -} - -// DiffFile represents a file diff. -type DiffFile struct { - Name string - OldName string - Index int - Addition, Deletion int - Type DiffFileType - IsCreated bool - IsDeleted bool - IsBin bool - IsLFSFile bool - IsRenamed bool - IsSubmodule bool - Sections []*DiffSection - IsIncomplete bool -} - -// GetType returns type of diff file. -func (diffFile *DiffFile) GetType() int { - return int(diffFile.Type) -} - -// GetHighlightClass returns highlight class for a filename. -func (diffFile *DiffFile) GetHighlightClass() string { - return highlight.FileNameToHighlightClass(diffFile.Name) -} - -// Diff represents a difference between two git trees. -type Diff struct { - TotalAddition, TotalDeletion int - Files []*DiffFile - IsIncomplete bool -} - -// LoadComments loads comments into each line -func (diff *Diff) LoadComments(issue *Issue, currentUser *User) error { - allComments, err := FetchCodeComments(issue, currentUser) - if err != nil { - return err - } - for _, file := range diff.Files { - if lineCommits, ok := allComments[file.Name]; ok { - for _, section := range file.Sections { - for _, line := range section.Lines { - if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok { - line.Comments = append(line.Comments, comments...) - } - if comments, ok := lineCommits[int64(line.RightIdx)]; ok { - line.Comments = append(line.Comments, comments...) - } - sort.SliceStable(line.Comments, func(i, j int) bool { - return line.Comments[i].CreatedUnix < line.Comments[j].CreatedUnix - }) - } - } - } - } - return nil -} - -// NumFiles returns number of files changes in a diff. -func (diff *Diff) NumFiles() int { - return len(diff.Files) -} - -// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9] -var hunkRegex = regexp.MustCompile(`^@@ -(?P<beginOld>[0-9]+)(,(?P<endOld>[0-9]+))? \+(?P<beginNew>[0-9]+)(,(?P<endNew>[0-9]+))? @@`) - -func isHeader(lof string) bool { - return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++") -} - -// CutDiffAroundLine cuts a diff of a file in way that only the given line + numberOfLine above it will be shown -// it also recalculates hunks and adds the appropriate headers to the new diff. -// Warning: Only one-file diffs are allowed. -func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLine int) string { - if line == 0 || numbersOfLine == 0 { - // no line or num of lines => no diff - return "" - } - scanner := bufio.NewScanner(originalDiff) - hunk := make([]string, 0) - // begin is the start of the hunk containing searched line - // end is the end of the hunk ... - // currentLine is the line number on the side of the searched line (differentiated by old) - // otherLine is the line number on the opposite side of the searched line (differentiated by old) - var begin, end, currentLine, otherLine int64 - var headerLines int - for scanner.Scan() { - lof := scanner.Text() - // Add header to enable parsing - if isHeader(lof) { - hunk = append(hunk, lof) - headerLines++ - } - if currentLine > line { - break - } - // Detect "hunk" with contains commented lof - if strings.HasPrefix(lof, "@@") { - // Already got our hunk. End of hunk detected! - if len(hunk) > headerLines { - break - } - // A map with named groups of our regex to recognize them later more easily - submatches := hunkRegex.FindStringSubmatch(lof) - groups := make(map[string]string) - for i, name := range hunkRegex.SubexpNames() { - if i != 0 && name != "" { - groups[name] = submatches[i] - } - } - if old { - begin = com.StrTo(groups["beginOld"]).MustInt64() - end = com.StrTo(groups["endOld"]).MustInt64() - // init otherLine with begin of opposite side - otherLine = com.StrTo(groups["beginNew"]).MustInt64() - } else { - begin = com.StrTo(groups["beginNew"]).MustInt64() - if groups["endNew"] != "" { - end = com.StrTo(groups["endNew"]).MustInt64() - } else { - end = 0 - } - // init otherLine with begin of opposite side - otherLine = com.StrTo(groups["beginOld"]).MustInt64() - } - end += begin // end is for real only the number of lines in hunk - // lof is between begin and end - if begin <= line && end >= line { - hunk = append(hunk, lof) - currentLine = begin - continue - } - } else if len(hunk) > headerLines { - hunk = append(hunk, lof) - // Count lines in context - switch lof[0] { - case '+': - if !old { - currentLine++ - } else { - otherLine++ - } - case '-': - if old { - currentLine++ - } else { - otherLine++ - } - default: - currentLine++ - otherLine++ - } - } - } - - // No hunk found - if currentLine == 0 { - return "" - } - // headerLines + hunkLine (1) = totalNonCodeLines - if len(hunk)-headerLines-1 <= numbersOfLine { - // No need to cut the hunk => return existing hunk - return strings.Join(hunk, "\n") - } - var oldBegin, oldNumOfLines, newBegin, newNumOfLines int64 - if old { - oldBegin = currentLine - newBegin = otherLine - } else { - oldBegin = otherLine - newBegin = currentLine - } - // headers + hunk header - newHunk := make([]string, headerLines) - // transfer existing headers - copy(newHunk, hunk[:headerLines]) - // transfer last n lines - newHunk = append(newHunk, hunk[len(hunk)-numbersOfLine-1:]...) - // calculate newBegin, ... by counting lines - for i := len(hunk) - 1; i >= len(hunk)-numbersOfLine; i-- { - switch hunk[i][0] { - case '+': - newBegin-- - newNumOfLines++ - case '-': - oldBegin-- - oldNumOfLines++ - default: - oldBegin-- - newBegin-- - newNumOfLines++ - oldNumOfLines++ - } - } - // construct the new hunk header - newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@", - oldBegin, oldNumOfLines, newBegin, newNumOfLines) - return strings.Join(newHunk, "\n") -} - -const cmdDiffHead = "diff --git " - -// ParsePatch builds a Diff object from a io.Reader and some -// parameters. -// TODO: move this function to gogits/git-module -func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*Diff, error) { - var ( - diff = &Diff{Files: make([]*DiffFile, 0)} - - curFile = &DiffFile{} - curSection = &DiffSection{ - Lines: make([]*DiffLine, 0, 10), - } - - leftLine, rightLine int - lineCount int - curFileLinesCount int - curFileLFSPrefix bool - ) - - input := bufio.NewReader(reader) - isEOF := false - for !isEOF { - var linebuf bytes.Buffer - for { - b, err := input.ReadByte() - if err != nil { - if err == io.EOF { - isEOF = true - break - } else { - return nil, fmt.Errorf("ReadByte: %v", err) - } - } - if b == '\n' { - break - } - if linebuf.Len() < maxLineCharacters { - linebuf.WriteByte(b) - } else if linebuf.Len() == maxLineCharacters { - curFile.IsIncomplete = true - } - } - line := linebuf.String() - - if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 { - continue - } - - trimLine := strings.Trim(line, "+- ") - - if trimLine == LFSMetaFileIdentifier { - curFileLFSPrefix = true - } - - if curFileLFSPrefix && strings.HasPrefix(trimLine, LFSMetaFileOidPrefix) { - oid := strings.TrimPrefix(trimLine, LFSMetaFileOidPrefix) - - if len(oid) == 64 { - m := &LFSMetaObject{Oid: oid} - count, err := x.Count(m) - - if err == nil && count > 0 { - curFile.IsBin = true - curFile.IsLFSFile = true - curSection.Lines = nil - } - } - } - - curFileLinesCount++ - lineCount++ - - // Diff data too large, we only show the first about maxLines lines - if curFileLinesCount >= maxLines { - curFile.IsIncomplete = true - } - switch { - case line[0] == ' ': - diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine} - leftLine++ - rightLine++ - curSection.Lines = append(curSection.Lines, diffLine) - continue - 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 - } - continue - case line[0] == '+': - curFile.Addition++ - diff.TotalAddition++ - diffLine := &DiffLine{Type: DiffLineAdd, Content: line, RightIdx: rightLine} - rightLine++ - curSection.Lines = append(curSection.Lines, diffLine) - continue - case line[0] == '-': - curFile.Deletion++ - diff.TotalDeletion++ - diffLine := &DiffLine{Type: DiffLineDel, Content: line, LeftIdx: leftLine} - if leftLine > 0 { - leftLine++ - } - curSection.Lines = append(curSection.Lines, diffLine) - case strings.HasPrefix(line, "Binary"): - curFile.IsBin = true - continue - } - - // Get new file. - if strings.HasPrefix(line, cmdDiffHead) { - var middle int - - // Note: In case file name is surrounded by double quotes (it happens only in git-shell). - // e.g. diff --git "a/xxx" "b/xxx" - hasQuote := line[len(cmdDiffHead)] == '"' - if hasQuote { - middle = strings.Index(line, ` "b/`) - } else { - middle = strings.Index(line, " b/") - } - - beg := len(cmdDiffHead) - a := line[beg+2 : middle] - b := line[middle+3:] - - if hasQuote { - // Keep the entire string in double quotes for now - a = line[beg:middle] - b = line[middle+1:] - - var err error - a, err = strconv.Unquote(a) - if err != nil { - return nil, fmt.Errorf("Unquote: %v", err) - } - b, err = strconv.Unquote(b) - if err != nil { - return nil, fmt.Errorf("Unquote: %v", err) - } - // Now remove the /a /b - a = a[2:] - b = b[2:] - - } - - curFile = &DiffFile{ - Name: b, - OldName: a, - Index: len(diff.Files) + 1, - Type: DiffFileChange, - Sections: make([]*DiffSection, 0, 10), - IsRenamed: a != b, - } - diff.Files = append(diff.Files, curFile) - if len(diff.Files) >= maxFiles { - diff.IsIncomplete = true - _, err := io.Copy(ioutil.Discard, reader) - if err != nil { - return nil, fmt.Errorf("Copy: %v", err) - } - break - } - curFileLinesCount = 0 - curFileLFSPrefix = false - - // Check file diff type and is submodule. - for { - line, err := input.ReadString('\n') - if err != nil { - if err == io.EOF { - isEOF = true - } else { - return nil, fmt.Errorf("ReadString: %v", err) - } - } - - switch { - case strings.HasPrefix(line, "new file"): - curFile.Type = DiffFileAdd - curFile.IsCreated = true - case strings.HasPrefix(line, "deleted"): - curFile.Type = DiffFileDel - curFile.IsDeleted = true - case strings.HasPrefix(line, "index"): - curFile.Type = DiffFileChange - case strings.HasPrefix(line, "similarity index 100%"): - curFile.Type = DiffFileRename - } - if curFile.Type > 0 { - if strings.HasSuffix(line, " 160000\n") { - curFile.IsSubmodule = true - } - break - } - } - } - } - - // FIXME: detect encoding while parsing. - var buf bytes.Buffer - for _, f := range diff.Files { - buf.Reset() - for _, sec := range f.Sections { - for _, l := range sec.Lines { - buf.WriteString(l.Content) - buf.WriteString("\n") - } - } - charsetLabel, err := charset.DetectEncoding(buf.Bytes()) - if charsetLabel != "UTF-8" && err == nil { - encoding, _ := stdcharset.Lookup(charsetLabel) - if encoding != nil { - d := encoding.NewDecoder() - for _, sec := range f.Sections { - for _, l := range sec.Lines { - if c, _, err := transform.String(d, l.Content); err == nil { - l.Content = c - } - } - } - } - } - } - - return diff, nil -} - -// GetDiffRange builds a Diff between two commits of a repository. -// passing the empty string as beforeCommitID returns a diff from the -// parent commit. -func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) { - return GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID, maxLines, maxLineCharacters, maxFiles, "") -} - -// GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository. -// Passing the empty string as beforeCommitID returns a diff from the parent commit. -// The whitespaceBehavior is either an empty string or a git flag -func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string) (*Diff, error) { - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - return nil, err - } - - commit, err := gitRepo.GetCommit(afterCommitID) - if err != nil { - return nil, err - } - - var cmd *exec.Cmd - if len(beforeCommitID) == 0 && commit.ParentCount() == 0 { - cmd = exec.Command(git.GitExecutable, "show", afterCommitID) - } else { - actualBeforeCommitID := beforeCommitID - if len(actualBeforeCommitID) == 0 { - parentCommit, _ := commit.Parent(0) - actualBeforeCommitID = parentCommit.ID.String() - } - diffArgs := []string{"diff", "-M"} - if len(whitespaceBehavior) != 0 { - diffArgs = append(diffArgs, whitespaceBehavior) - } - diffArgs = append(diffArgs, actualBeforeCommitID) - diffArgs = append(diffArgs, afterCommitID) - cmd = exec.Command(git.GitExecutable, diffArgs...) - } - cmd.Dir = repoPath - cmd.Stderr = os.Stderr - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, fmt.Errorf("StdoutPipe: %v", err) - } - - if err = cmd.Start(); err != nil { - return nil, fmt.Errorf("Start: %v", err) - } - - pid := process.GetManager().Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd) - defer process.GetManager().Remove(pid) - - diff, err := ParsePatch(maxLines, maxLineCharacters, maxFiles, stdout) - if err != nil { - return nil, fmt.Errorf("ParsePatch: %v", err) - } - - if err = cmd.Wait(); err != nil { - return nil, fmt.Errorf("Wait: %v", err) - } - - return diff, nil -} - -// RawDiffType type of a raw diff. -type RawDiffType string - -// RawDiffType possible values. -const ( - RawDiffNormal RawDiffType = "diff" - RawDiffPatch RawDiffType = "patch" -) - -// GetRawDiff dumps diff results of repository in given commit ID to io.Writer. -// TODO: move this function to gogits/git-module -func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error { - return GetRawDiffForFile(repoPath, "", commitID, diffType, "", writer) -} - -// GetRawDiffForFile dumps diff results of file in given commit ID to io.Writer. -// TODO: move this function to gogits/git-module -func GetRawDiffForFile(repoPath, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error { - repo, err := git.OpenRepository(repoPath) - if err != nil { - return fmt.Errorf("OpenRepository: %v", err) - } - - commit, err := repo.GetCommit(endCommit) - if err != nil { - return fmt.Errorf("GetCommit: %v", err) - } - fileArgs := make([]string, 0) - if len(file) > 0 { - fileArgs = append(fileArgs, "--", file) - } - var cmd *exec.Cmd - switch diffType { - case RawDiffNormal: - if len(startCommit) != 0 { - cmd = exec.Command(git.GitExecutable, append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...) - } else if commit.ParentCount() == 0 { - cmd = exec.Command(git.GitExecutable, append([]string{"show", endCommit}, fileArgs...)...) - } else { - c, _ := commit.Parent(0) - cmd = exec.Command(git.GitExecutable, append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...) - } - case RawDiffPatch: - if len(startCommit) != 0 { - query := fmt.Sprintf("%s...%s", endCommit, startCommit) - cmd = exec.Command(git.GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...) - } else if commit.ParentCount() == 0 { - cmd = exec.Command(git.GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...) - } else { - c, _ := commit.Parent(0) - query := fmt.Sprintf("%s...%s", endCommit, c.ID.String()) - cmd = exec.Command(git.GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...) - } - default: - return fmt.Errorf("invalid diffType: %s", diffType) - } - - stderr := new(bytes.Buffer) - - cmd.Dir = repoPath - cmd.Stdout = writer - cmd.Stderr = stderr - if err = cmd.Run(); err != nil { - return fmt.Errorf("Run: %v - %s", err, stderr) - } - return nil -} - -// GetDiffCommit builds a Diff representing the given commitID. -func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) { - return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacters, maxFiles) -} diff --git a/models/git_diff_test.go b/models/git_diff_test.go deleted file mode 100644 index bf52095acf..0000000000 --- a/models/git_diff_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package models - -import ( - "html/template" - "strings" - "testing" - - "code.gitea.io/gitea/modules/setting" - - dmp "github.com/sergi/go-diff/diffmatchpatch" - "github.com/stretchr/testify/assert" -) - -func assertEqual(t *testing.T, s1 string, s2 template.HTML) { - if s1 != string(s2) { - t.Errorf("%s should be equal %s", s2, s1) - } -} - -func TestDiffToHTML(t *testing.T) { - assertEqual(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{ - {Type: dmp.DiffEqual, Text: "foo "}, - {Type: dmp.DiffInsert, Text: "bar"}, - {Type: dmp.DiffDelete, Text: " baz"}, - {Type: dmp.DiffEqual, Text: " biz"}, - }, DiffLineAdd)) - - assertEqual(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{ - {Type: dmp.DiffEqual, Text: "foo "}, - {Type: dmp.DiffDelete, Text: "bar"}, - {Type: dmp.DiffInsert, Text: " baz"}, - {Type: dmp.DiffEqual, Text: " biz"}, - }, DiffLineDel)) -} - -const exampleDiff = `diff --git a/README.md b/README.md ---- a/README.md -+++ b/README.md -@@ -1,3 +1,6 @@ - # gitea-github-migrator -+ -+ Build Status -- Latest Release - Docker Pulls -+ cut off -+ cut off` - -func TestCutDiffAroundLine(t *testing.T) { - result := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3) - resultByLine := strings.Split(result, "\n") - assert.Len(t, resultByLine, 7) - // Check if headers got transferred - assert.Equal(t, "diff --git a/README.md b/README.md", resultByLine[0]) - assert.Equal(t, "--- a/README.md", resultByLine[1]) - assert.Equal(t, "+++ b/README.md", resultByLine[2]) - // Check if hunk header is calculated correctly - assert.Equal(t, "@@ -2,2 +3,2 @@", resultByLine[3]) - // Check if line got transferred - assert.Equal(t, "+ Build Status", resultByLine[4]) - - // Must be same result as before since old line 3 == new line 5 - newResult := CutDiffAroundLine(strings.NewReader(exampleDiff), 3, true, 3) - assert.Equal(t, result, newResult, "Must be same result as before since old line 3 == new line 5") - - newResult = CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 300) - assert.Equal(t, exampleDiff, newResult) - - emptyResult := CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 0) - assert.Empty(t, emptyResult) - - // Line is out of scope - emptyResult = CutDiffAroundLine(strings.NewReader(exampleDiff), 434, false, 0) - assert.Empty(t, emptyResult) -} - -func BenchmarkCutDiffAroundLine(b *testing.B) { - for n := 0; n < b.N; n++ { - CutDiffAroundLine(strings.NewReader(exampleDiff), 3, true, 3) - } -} - -func ExampleCutDiffAroundLine() { - const diff = `diff --git a/README.md b/README.md ---- a/README.md -+++ b/README.md -@@ -1,3 +1,6 @@ - # gitea-github-migrator -+ -+ Build Status -- Latest Release - Docker Pulls -+ cut off -+ cut off` - result := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3) - println(result) -} - -func TestParsePatch(t *testing.T) { - var diff = `diff --git "a/README.md" "b/README.md" ---- a/README.md -+++ b/README.md -@@ -1,3 +1,6 @@ - # gitea-github-migrator -+ -+ Build Status -- Latest Release - Docker Pulls -+ cut off -+ cut off` - result, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff)) - if err != nil { - t.Errorf("ParsePatch failed: %s", err) - } - println(result) - - var diff2 = `diff --git "a/A \\ B" "b/A \\ B" ---- "a/A \\ B" -+++ "b/A \\ B" -@@ -1,3 +1,6 @@ - # gitea-github-migrator -+ -+ Build Status -- Latest Release - Docker Pulls -+ cut off -+ cut off` - result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff2)) - if err != nil { - t.Errorf("ParsePatch failed: %s", err) - } - println(result) - - var diff3 = `diff --git a/README.md b/README.md ---- a/README.md -+++ b/README.md -@@ -1,3 +1,6 @@ - # gitea-github-migrator -+ -+ Build Status -- Latest Release - Docker Pulls -+ cut off -+ cut off` - result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff3)) - if err != nil { - t.Errorf("ParsePatch failed: %s", err) - } - println(result) -} - -func setupDefaultDiff() *Diff { - return &Diff{ - Files: []*DiffFile{ - { - Name: "README.md", - Sections: []*DiffSection{ - { - Lines: []*DiffLine{ - { - LeftIdx: 4, - RightIdx: 4, - }, - }, - }, - }, - }, - }, - } -} -func TestDiff_LoadComments(t *testing.T) { - issue := AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) - user := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - diff := setupDefaultDiff() - assert.NoError(t, PrepareTestDatabase()) - assert.NoError(t, diff.LoadComments(issue, user)) - assert.Len(t, diff.Files[0].Sections[0].Lines[0].Comments, 2) -} - -func TestDiffLine_CanComment(t *testing.T) { - assert.False(t, (&DiffLine{Type: DiffLineSection}).CanComment()) - assert.False(t, (&DiffLine{Type: DiffLineAdd, Comments: []*Comment{{Content: "bla"}}}).CanComment()) - assert.True(t, (&DiffLine{Type: DiffLineAdd}).CanComment()) - assert.True(t, (&DiffLine{Type: DiffLineDel}).CanComment()) - assert.True(t, (&DiffLine{Type: DiffLinePlain}).CanComment()) -} - -func TestDiffLine_GetCommentSide(t *testing.T) { - assert.Equal(t, "previous", (&DiffLine{Comments: []*Comment{{Line: -3}}}).GetCommentSide()) - assert.Equal(t, "proposed", (&DiffLine{Comments: []*Comment{{Line: 3}}}).GetCommentSide()) -} diff --git a/models/issue_comment.go b/models/issue_comment.go index ad1a59e9d3..2a9e8596cb 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -7,7 +7,6 @@ package models import ( - "bytes" "fmt" "strings" @@ -15,7 +14,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -488,32 +486,6 @@ func (c *Comment) UnsignedLine() uint64 { return uint64(c.Line) } -// AsDiff returns c.Patch as *Diff -func (c *Comment) AsDiff() (*Diff, error) { - diff, err := ParsePatch(setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.Patch)) - if err != nil { - return nil, err - } - if len(diff.Files) == 0 { - return nil, fmt.Errorf("no file found for comment ID: %d", c.ID) - } - secs := diff.Files[0].Sections - if len(secs) == 0 { - return nil, fmt.Errorf("no sections found for comment ID: %d", c.ID) - } - return diff, nil -} - -// MustAsDiff executes AsDiff and logs the error instead of returning -func (c *Comment) MustAsDiff() *Diff { - diff, err := c.AsDiff() - if err != nil { - log.Warn("MustAsDiff: %v", err) - } - return diff -} - // CodeCommentURL returns the url to a comment in code func (c *Comment) CodeCommentURL() string { err := c.LoadIssue() @@ -873,59 +845,6 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri return comment, nil } -// CreateCodeComment creates a plain code comment at the specified line / path -func CreateCodeComment(doer *User, repo *Repository, issue *Issue, content, treePath string, line, reviewID int64) (*Comment, error) { - var commitID, patch string - pr, err := GetPullRequestByIssueID(issue.ID) - if err != nil { - return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err) - } - if err := pr.GetBaseRepo(); err != nil { - return nil, fmt.Errorf("GetHeadRepo: %v", err) - } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) - if err != nil { - return nil, fmt.Errorf("OpenRepository: %v", err) - } - - // FIXME validate treePath - // Get latest commit referencing the commented line - // No need for get commit for base branch changes - 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") { - 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) - } - patchBuf := new(bytes.Buffer) - if err := GetRawDiffForFile(gitRepo.Path, pr.MergeBase, headCommitID, RawDiffNormal, treePath, patchBuf); err != nil { - return nil, fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", err, gitRepo.Path, pr.MergeBase, headCommitID, treePath) - } - patch = CutDiffAroundLine(patchBuf, int64((&Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines) - } - return CreateComment(&CreateCommentOptions{ - Type: CommentTypeCode, - Doer: doer, - Repo: repo, - Issue: issue, - Content: content, - LineNum: line, - TreePath: treePath, - CommitSHA: commitID, - ReviewID: reviewID, - Patch: patch, - }) -} - // CreateRefComment creates a commit reference comment to issue. func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error { if len(commitSHA) == 0 { diff --git a/models/models.go b/models/models.go index 04acc77aa9..e802a35a77 100644 --- a/models/models.go +++ b/models/models.go @@ -250,3 +250,8 @@ func MaxBatchInsertSize(bean interface{}) int { t := x.TableInfo(bean) return 999 / len(t.ColumnsSeq()) } + +// Count returns records number according struct's fields as database query conditions +func Count(bean interface{}) (int64, error) { + return x.Count(bean) +} |