models/repo_diff: move core functions to gogits/git-module
parent
16c6ca72cd
commit
55afc1ad21
2
gogs.go
2
gogs.go
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const APP_VER = "0.11.1.0403"
|
||||
const APP_VER = "0.11.2.0404"
|
||||
|
||||
func init() {
|
||||
setting.AppVer = APP_VER
|
||||
|
|
|
@ -5,63 +5,25 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/text/transform"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/template/highlight"
|
||||
)
|
||||
|
||||
type DiffLineType uint8
|
||||
|
||||
const (
|
||||
DIFF_LINE_PLAIN DiffLineType = iota + 1
|
||||
DIFF_LINE_ADD
|
||||
DIFF_LINE_DEL
|
||||
DIFF_LINE_SECTION
|
||||
)
|
||||
|
||||
type DiffFileType uint8
|
||||
|
||||
const (
|
||||
DIFF_FILE_ADD DiffFileType = iota + 1
|
||||
DIFF_FILE_CHANGE
|
||||
DIFF_FILE_DEL
|
||||
DIFF_FILE_RENAME
|
||||
)
|
||||
|
||||
type DiffLine struct {
|
||||
LeftIdx int
|
||||
RightIdx int
|
||||
Type DiffLineType
|
||||
Content string
|
||||
}
|
||||
|
||||
func (d *DiffLine) GetType() int {
|
||||
return int(d.Type)
|
||||
}
|
||||
|
||||
type DiffSection struct {
|
||||
Name string
|
||||
Lines []*DiffLine
|
||||
*git.DiffSection
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -70,24 +32,24 @@ var (
|
|||
codeTagSuffix = []byte("</span>")
|
||||
)
|
||||
|
||||
func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
|
||||
func diffToHTML(diffs []diffmatchpatch.Diff, lineType git.DiffLineType) template.HTML {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Reproduce signs which are cutted for inline diff before.
|
||||
switch lineType {
|
||||
case DIFF_LINE_ADD:
|
||||
case git.DIFF_LINE_ADD:
|
||||
buf.WriteByte('+')
|
||||
case DIFF_LINE_DEL:
|
||||
case git.DIFF_LINE_DEL:
|
||||
buf.WriteByte('-')
|
||||
}
|
||||
|
||||
for i := range diffs {
|
||||
switch {
|
||||
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD:
|
||||
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == git.DIFF_LINE_ADD:
|
||||
buf.Write(addedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL:
|
||||
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == git.DIFF_LINE_DEL:
|
||||
buf.Write(removedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
|
@ -99,77 +61,34 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
|
|||
return template.HTML(buf.Bytes())
|
||||
}
|
||||
|
||||
// get an 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 DIFF_LINE_ADD:
|
||||
addCount++
|
||||
case DIFF_LINE_DEL:
|
||||
delCount++
|
||||
default:
|
||||
if matchDiffLine != nil {
|
||||
break LOOP
|
||||
}
|
||||
difference = diffLine.RightIdx - diffLine.LeftIdx
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
}
|
||||
|
||||
switch lineType {
|
||||
case DIFF_LINE_DEL:
|
||||
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
case DIFF_LINE_ADD:
|
||||
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
|
||||
}
|
||||
|
||||
// computes inline diff for the given line
|
||||
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
|
||||
// ComputedInlineDiffFor computes inline diff for the given line.
|
||||
func (diffSection *DiffSection) ComputedInlineDiffFor(diffLine *git.DiffLine) template.HTML {
|
||||
if setting.Git.DisableDiffHighlight {
|
||||
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
||||
}
|
||||
var (
|
||||
compareDiffLine *DiffLine
|
||||
compareDiffLine *git.DiffLine
|
||||
diff1 string
|
||||
diff2 string
|
||||
)
|
||||
|
||||
// try to find equivalent diff line. ignore, otherwise
|
||||
switch diffLine.Type {
|
||||
case DIFF_LINE_ADD:
|
||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx)
|
||||
case git.DIFF_LINE_ADD:
|
||||
compareDiffLine = diffSection.Line(git.DIFF_LINE_DEL, diffLine.RightIdx)
|
||||
if compareDiffLine == nil {
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
diff1 = compareDiffLine.Content
|
||||
diff2 = diffLine.Content
|
||||
case DIFF_LINE_DEL:
|
||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx)
|
||||
case git.DIFF_LINE_DEL:
|
||||
compareDiffLine = diffSection.Line(git.DIFF_LINE_ADD, diffLine.LeftIdx)
|
||||
if compareDiffLine == nil {
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
|
@ -186,336 +105,90 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem
|
|||
}
|
||||
|
||||
type DiffFile struct {
|
||||
Name string
|
||||
OldName string
|
||||
Index string // 40-byte SHA, Changed/New: new SHA; Deleted: old SHA
|
||||
Addition, Deletion int
|
||||
Type DiffFileType
|
||||
IsCreated bool
|
||||
IsDeleted bool
|
||||
IsBin bool
|
||||
IsRenamed bool
|
||||
IsSubmodule bool
|
||||
Sections []*DiffSection
|
||||
IsIncomplete bool
|
||||
*git.DiffFile
|
||||
Sections []*DiffSection
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) GetType() int {
|
||||
return int(diffFile.Type)
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) GetHighlightClass() string {
|
||||
func (diffFile *DiffFile) HighlightClass() string {
|
||||
return highlight.FileNameToHighlightClass(diffFile.Name)
|
||||
}
|
||||
|
||||
type Diff struct {
|
||||
TotalAddition, TotalDeletion int
|
||||
Files []*DiffFile
|
||||
IsIncomplete bool
|
||||
*git.Diff
|
||||
Files []*DiffFile
|
||||
}
|
||||
|
||||
func (diff *Diff) NumFiles() int {
|
||||
return len(diff.Files)
|
||||
}
|
||||
|
||||
const DIFF_HEAD = "diff --git "
|
||||
|
||||
// TODO: move this function to gogits/git-module
|
||||
func ParsePatch(maxLines, maxLineCharacteres, 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
|
||||
)
|
||||
|
||||
input := bufio.NewReader(reader)
|
||||
isEOF := false
|
||||
for !isEOF {
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
isEOF = true
|
||||
} else {
|
||||
return nil, fmt.Errorf("ReadString: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(line) > 0 && line[len(line)-1] == '\n' {
|
||||
// Remove line break.
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
curFileLinesCount++
|
||||
lineCount++
|
||||
|
||||
// Diff data too large, we only show the first about maxlines lines
|
||||
if curFileLinesCount >= maxLines || len(line) >= maxLineCharacteres {
|
||||
curFile.IsIncomplete = true
|
||||
}
|
||||
|
||||
switch {
|
||||
case line[0] == ' ':
|
||||
diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, 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: DIFF_LINE_SECTION, 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: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
|
||||
rightLine++
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
continue
|
||||
case line[0] == '-':
|
||||
curFile.Deletion++
|
||||
diff.TotalDeletion++
|
||||
diffLine := &DiffLine{Type: DIFF_LINE_DEL, 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, DIFF_HEAD) {
|
||||
middle := -1
|
||||
|
||||
// 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(DIFF_HEAD)] == '"'
|
||||
if hasQuote {
|
||||
middle = strings.Index(line, ` "b/`)
|
||||
} else {
|
||||
middle = strings.Index(line, " b/")
|
||||
}
|
||||
|
||||
beg := len(DIFF_HEAD)
|
||||
a := line[beg+2 : middle]
|
||||
b := line[middle+3:]
|
||||
if hasQuote {
|
||||
a = string(git.UnescapeChars([]byte(a[1 : len(a)-1])))
|
||||
b = string(git.UnescapeChars([]byte(b[1 : len(b)-1])))
|
||||
}
|
||||
|
||||
curFile = &DiffFile{
|
||||
Name: a,
|
||||
Type: DIFF_FILE_CHANGE,
|
||||
Sections: make([]*DiffSection, 0, 10),
|
||||
}
|
||||
diff.Files = append(diff.Files, curFile)
|
||||
if len(diff.Files) >= maxFiles {
|
||||
diff.IsIncomplete = true
|
||||
io.Copy(ioutil.Discard, reader)
|
||||
break
|
||||
}
|
||||
curFileLinesCount = 0
|
||||
|
||||
// Check file diff type and submodule.
|
||||
CHECK_TYPE:
|
||||
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 = DIFF_FILE_ADD
|
||||
curFile.IsCreated = true
|
||||
curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
|
||||
case strings.HasPrefix(line, "deleted"):
|
||||
curFile.Type = DIFF_FILE_DEL
|
||||
curFile.IsDeleted = true
|
||||
curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
|
||||
case strings.HasPrefix(line, "index"):
|
||||
if curFile.IsDeleted {
|
||||
curFile.Index = line[6:46]
|
||||
} else if len(line) >= 88 {
|
||||
curFile.Index = line[49:88]
|
||||
} else {
|
||||
curFile.Index = curFile.Name
|
||||
}
|
||||
break CHECK_TYPE
|
||||
case strings.HasPrefix(line, "similarity index 100%"):
|
||||
curFile.Type = DIFF_FILE_RENAME
|
||||
curFile.IsRenamed = true
|
||||
curFile.OldName = curFile.Name
|
||||
curFile.Name = b
|
||||
curFile.Index = b
|
||||
break CHECK_TYPE
|
||||
}
|
||||
}
|
||||
}
|
||||
func NewDiff(gitDiff *git.Diff) *Diff {
|
||||
diff := &Diff{
|
||||
Diff: gitDiff,
|
||||
Files: make([]*DiffFile, gitDiff.NumFiles()),
|
||||
}
|
||||
|
||||
// FIXME: detect encoding while parsing.
|
||||
var buf bytes.Buffer
|
||||
for _, f := range diff.Files {
|
||||
for i := range gitDiff.Files {
|
||||
buf.Reset()
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
buf.WriteString(l.Content)
|
||||
|
||||
diff.Files[i] = &DiffFile{
|
||||
DiffFile: gitDiff.Files[i],
|
||||
Sections: make([]*DiffSection, gitDiff.Files[i].NumSections()),
|
||||
}
|
||||
|
||||
for j := range gitDiff.Files[i].Sections {
|
||||
diff.Files[i].Sections[j] = &DiffSection{
|
||||
DiffSection: gitDiff.Files[i].Sections[j],
|
||||
}
|
||||
|
||||
for k := range diff.Files[i].Sections[j].Lines {
|
||||
buf.WriteString(diff.Files[i].Sections[j].Lines[k].Content)
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
charsetLabel, err := base.DetectEncoding(buf.Bytes())
|
||||
if charsetLabel != "UTF-8" && err == nil {
|
||||
encoding, _ := charset.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
|
||||
for j := range diff.Files[i].Sections {
|
||||
for k := range diff.Files[i].Sections[j].Lines {
|
||||
if c, _, err := transform.String(d, diff.Files[i].Sections[j].Lines[k].Content); err == nil {
|
||||
diff.Files[i].Sections[j].Lines[k].Content = c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return diff, nil
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) {
|
||||
done := make(chan error)
|
||||
var gitDiff *git.Diff
|
||||
go func() {
|
||||
gitDiff = git.ParsePatch(done, maxLines, maxLineCharacteres, maxFiles, reader)
|
||||
}()
|
||||
|
||||
if err := <-done; err != nil {
|
||||
return nil, fmt.Errorf("ParsePatch: %v", err)
|
||||
}
|
||||
return NewDiff(gitDiff), nil
|
||||
}
|
||||
|
||||
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
gitDiff, err := git.GetDiffRange(repoPath, beforeCommitID, afterCommitID, maxLines, maxLineCharacteres, maxFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("GetDiffRange: %v", err)
|
||||
}
|
||||
|
||||
commit, err := gitRepo.GetCommit(afterCommitID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
// if "after" commit given
|
||||
if len(beforeCommitID) == 0 {
|
||||
// First commit of repository.
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "show", "--full-index", afterCommitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd = exec.Command("git", "diff", "--full-index", "-M", c.ID.String(), afterCommitID)
|
||||
}
|
||||
} else {
|
||||
cmd = exec.Command("git", "diff", "--full-index", "-M", beforeCommitID, afterCommitID)
|
||||
}
|
||||
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.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd)
|
||||
defer process.Remove(pid)
|
||||
|
||||
diff, err := ParsePatch(maxLines, maxLineCharacteres, 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
|
||||
}
|
||||
|
||||
type RawDiffType string
|
||||
|
||||
const (
|
||||
RAW_DIFF_NORMAL RawDiffType = "diff"
|
||||
RAW_DIFF_PATCH 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 {
|
||||
repo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
commit, err := repo.GetCommit(commitID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetCommit: %v", err)
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
switch diffType {
|
||||
case RAW_DIFF_NORMAL:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "show", commitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd = exec.Command("git", "diff", "-M", c.ID.String(), commitID)
|
||||
}
|
||||
case RAW_DIFF_PATCH:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", "--root", commitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
query := fmt.Sprintf("%s...%s", commitID, c.ID.String())
|
||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", query)
|
||||
}
|
||||
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
|
||||
return NewDiff(gitDiff), nil
|
||||
}
|
||||
|
||||
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
|
||||
gitDiff, err := git.GetDiffCommit(repoPath, commitID, maxLines, maxLineCharacteres, maxFiles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetDiffCommit: %v", err)
|
||||
}
|
||||
return NewDiff(gitDiff), nil
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
// Copyright 2016 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 (
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"html/template"
|
||||
"testing"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
|
||||
|
@ -12,24 +18,24 @@ func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
|
|||
}
|
||||
}
|
||||
|
||||
func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) {
|
||||
func assertLineEqual(t *testing.T, d1 *git.DiffLine, d2 *git.DiffLine) {
|
||||
if d1 != d2 {
|
||||
t.Errorf("%v should be equal %v", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiffToHTML(t *testing.T) {
|
||||
func Test_diffToHTML(t *testing.T) {
|
||||
assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffInsert, "bar"},
|
||||
dmp.Diff{dmp.DiffDelete, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_ADD))
|
||||
}, git.DIFF_LINE_ADD))
|
||||
|
||||
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffDelete, "bar"},
|
||||
dmp.Diff{dmp.DiffInsert, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_DEL))
|
||||
}, git.DIFF_LINE_DEL))
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ func Diff(ctx *context.Context) {
|
|||
commitID, setting.Git.MaxGitDiffLines,
|
||||
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "GetDiffCommit", err)
|
||||
ctx.NotFoundOrServerError("GetDiffCommit", git.IsErrNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -180,10 +180,10 @@ func Diff(ctx *context.Context) {
|
|||
}
|
||||
|
||||
func RawDiff(ctx *context.Context) {
|
||||
if err := models.GetRawDiff(
|
||||
if err := git.GetRawDiff(
|
||||
models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name),
|
||||
ctx.Params(":sha"),
|
||||
models.RawDiffType(ctx.Params(":ext")),
|
||||
git.RawDiffType(ctx.Params(":ext")),
|
||||
ctx.Resp,
|
||||
); err != nil {
|
||||
ctx.Handle(500, "GetRawDiff", err)
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.11.1.0403
|
||||
0.11.2.0404
|
|
@ -89,27 +89,27 @@
|
|||
<table>
|
||||
<tbody>
|
||||
{{if $.IsSplitStyle}}
|
||||
{{$highlightClass := $file.GetHighlightClass}}
|
||||
{{$highlightClass := $file.HighlightClass}}
|
||||
{{range $j, $section := $file.Sections}}
|
||||
{{range $k, $line := $section.Lines}}
|
||||
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
|
||||
{{if eq .GetType 4}}
|
||||
<td class="lines-num"></td>
|
||||
<td colspan="3" class="lines-code">
|
||||
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</code></pre>
|
||||
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre>
|
||||
</td>
|
||||
{{else}}
|
||||
<td class="lines-num lines-num-old">
|
||||
<span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
|
||||
</td>
|
||||
<td class="lines-code halfwidth">
|
||||
<pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></pre>
|
||||
<pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.ComputedInlineDiffFor $line}}{{end}}</code></pre>
|
||||
</td>
|
||||
<td class="lines-num lines-num-new">
|
||||
<span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
|
||||
</td>
|
||||
<td class="lines-code halfwidth">
|
||||
<pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></pre>
|
||||
<pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.RightIdx}}{{$section.ComputedInlineDiffFor $line}}{{end}}</code></pre>
|
||||
</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{$file := .}}
|
||||
{{$highlightClass := $file.GetHighlightClass}}
|
||||
{{$highlightClass := $file.HighlightClass}}
|
||||
{{range $j, $section := $file.Sections}}
|
||||
{{range $k, $line := $section.Lines}}
|
||||
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
|
||||
|
@ -20,7 +20,7 @@
|
|||
</td>
|
||||
{{end}}
|
||||
<td class="lines-code">
|
||||
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</code></pre>
|
||||
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.ComputedInlineDiffFor $line}}</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
|
|
@ -271,10 +271,7 @@ func NewCommitFileStatus() *CommitFileStatus {
|
|||
// GetCommitFileStatus returns file status of commit in given repository.
|
||||
func GetCommitFileStatus(repoPath, commitID string) (*CommitFileStatus, error) {
|
||||
stdout, w := io.Pipe()
|
||||
defer stdout.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
|
||||
done := make(chan struct{})
|
||||
fileStatus := NewCommitFileStatus()
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
|
@ -293,12 +290,17 @@ func GetCommitFileStatus(repoPath, commitID string) (*CommitFileStatus, error) {
|
|||
fileStatus.Modified = append(fileStatus.Modified, fields[1])
|
||||
}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
if err := NewCommand("log", "-1", "--name-status", "--pretty=format:''", commitID).RunInDirPipeline(repoPath, w, stderr); err != nil {
|
||||
stderr := new(bytes.Buffer)
|
||||
err := NewCommand("log", "-1", "--name-status", "--pretty=format:''", commitID).RunInDirPipeline(repoPath, w, stderr)
|
||||
w.Close() // Close writer to exit parsing goroutine
|
||||
if err != nil {
|
||||
return nil, concatenateError(err, stderr.String())
|
||||
}
|
||||
|
||||
<-done
|
||||
return fileStatus, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const _VERSION = "0.5.1"
|
||||
const _VERSION = "0.6.0"
|
||||
|
||||
func Version() string {
|
||||
return _VERSION
|
||||
|
|
|
@ -0,0 +1,398 @@
|
|||
// Copyright 2017 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 git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DiffLineType represents the type of a line in diff.
|
||||
type DiffLineType uint8
|
||||
|
||||
const (
|
||||
DIFF_LINE_PLAIN DiffLineType = iota + 1
|
||||
DIFF_LINE_ADD
|
||||
DIFF_LINE_DEL
|
||||
DIFF_LINE_SECTION
|
||||
)
|
||||
|
||||
// DiffFileType represents the file status in diff.
|
||||
type DiffFileType uint8
|
||||
|
||||
const (
|
||||
DIFF_FILE_ADD DiffFileType = iota + 1
|
||||
DIFF_FILE_CHANGE
|
||||
DIFF_FILE_DEL
|
||||
DIFF_FILE_RENAME
|
||||
)
|
||||
|
||||
// DiffLine represents a line in diff.
|
||||
type DiffLine struct {
|
||||
LeftIdx int
|
||||
RightIdx int
|
||||
Type DiffLineType
|
||||
Content string
|
||||
}
|
||||
|
||||
func (d *DiffLine) GetType() int {
|
||||
return int(d.Type)
|
||||
}
|
||||
|
||||
// DiffSection represents a section in diff.
|
||||
type DiffSection struct {
|
||||
Name string
|
||||
Lines []*DiffLine
|
||||
}
|
||||
|
||||
// Line returns a specific line by type (add or del) and file line number from a section.
|
||||
func (diffSection *DiffSection) Line(lineType DiffLineType, idx int) *DiffLine {
|
||||
var (
|
||||
difference = 0
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
matchDiffLine *DiffLine
|
||||
)
|
||||
|
||||
LOOP:
|
||||
for _, diffLine := range diffSection.Lines {
|
||||
switch diffLine.Type {
|
||||
case DIFF_LINE_ADD:
|
||||
addCount++
|
||||
case DIFF_LINE_DEL:
|
||||
delCount++
|
||||
default:
|
||||
if matchDiffLine != nil {
|
||||
break LOOP
|
||||
}
|
||||
difference = diffLine.RightIdx - diffLine.LeftIdx
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
}
|
||||
|
||||
switch lineType {
|
||||
case DIFF_LINE_DEL:
|
||||
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
case DIFF_LINE_ADD:
|
||||
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if addCount == delCount {
|
||||
return matchDiffLine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiffFile represents a file in diff.
|
||||
type DiffFile struct {
|
||||
Name string
|
||||
OldName string
|
||||
Index string // 40-byte SHA, Changed/New: new SHA; Deleted: old SHA
|
||||
Addition, Deletion int
|
||||
Type DiffFileType
|
||||
IsCreated bool
|
||||
IsDeleted bool
|
||||
IsBin bool
|
||||
IsRenamed bool
|
||||
IsSubmodule bool
|
||||
Sections []*DiffSection
|
||||
IsIncomplete bool
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) GetType() int {
|
||||
return int(diffFile.Type)
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) NumSections() int {
|
||||
return len(diffFile.Sections)
|
||||
}
|
||||
|
||||
// Diff contains all information of a specific diff output.
|
||||
type Diff struct {
|
||||
TotalAddition, TotalDeletion int
|
||||
Files []*DiffFile
|
||||
IsIncomplete bool
|
||||
}
|
||||
|
||||
func (diff *Diff) NumFiles() int {
|
||||
return len(diff.Files)
|
||||
}
|
||||
|
||||
const _DIFF_HEAD = "diff --git "
|
||||
|
||||
// ParsePatch takes a reader and parses everything it receives in diff format.
|
||||
func ParsePatch(done chan<- error, maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) *Diff {
|
||||
var (
|
||||
diff = &Diff{Files: make([]*DiffFile, 0)}
|
||||
|
||||
curFile *DiffFile
|
||||
curSection = &DiffSection{
|
||||
Lines: make([]*DiffLine, 0, 10),
|
||||
}
|
||||
|
||||
leftLine, rightLine int
|
||||
lineCount int
|
||||
curFileLinesCount int
|
||||
)
|
||||
input := bufio.NewReader(reader)
|
||||
isEOF := false
|
||||
for !isEOF {
|
||||
// TODO: would input.ReadBytes be more memory-efficient?
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
isEOF = true
|
||||
} else {
|
||||
done <- fmt.Errorf("ReadString: %v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(line) > 0 && line[len(line)-1] == '\n' {
|
||||
// Remove line break.
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
curFileLinesCount++
|
||||
lineCount++
|
||||
|
||||
// Diff data too large, we only show the first about maxlines lines
|
||||
if curFileLinesCount >= maxLines || len(line) >= maxLineCharacteres {
|
||||
curFile.IsIncomplete = true
|
||||
}
|
||||
|
||||
switch {
|
||||
case line[0] == ' ':
|
||||
diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, 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: DIFF_LINE_SECTION, Content: line}
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
|
||||
// Parse line number.
|
||||
ranges := strings.Split(ss[1][1:], " ")
|
||||
leftLine, _ = strconv.Atoi(strings.Split(ranges[0], ",")[0][1:])
|
||||
if len(ranges) > 1 {
|
||||
rightLine, _ = strconv.Atoi(strings.Split(ranges[1], ",")[0])
|
||||
} else {
|
||||
rightLine = leftLine
|
||||
}
|
||||
continue
|
||||
case line[0] == '+':
|
||||
curFile.Addition++
|
||||
diff.TotalAddition++
|
||||
diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine}
|
||||
rightLine++
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
continue
|
||||
case line[0] == '-':
|
||||
curFile.Deletion++
|
||||
diff.TotalDeletion++
|
||||
diffLine := &DiffLine{Type: DIFF_LINE_DEL, 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, _DIFF_HEAD) {
|
||||
middle := -1
|
||||
|
||||
// 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(_DIFF_HEAD)] == '"'
|
||||
if hasQuote {
|
||||
middle = strings.Index(line, ` "b/`)
|
||||
} else {
|
||||
middle = strings.Index(line, " b/")
|
||||
}
|
||||
|
||||
beg := len(_DIFF_HEAD)
|
||||
a := line[beg+2 : middle]
|
||||
b := line[middle+3:]
|
||||
if hasQuote {
|
||||
a = string(UnescapeChars([]byte(a[1 : len(a)-1])))
|
||||
b = string(UnescapeChars([]byte(b[1 : len(b)-1])))
|
||||
}
|
||||
|
||||
curFile = &DiffFile{
|
||||
Name: a,
|
||||
Type: DIFF_FILE_CHANGE,
|
||||
Sections: make([]*DiffSection, 0, 10),
|
||||
}
|
||||
diff.Files = append(diff.Files, curFile)
|
||||
if len(diff.Files) >= maxFiles {
|
||||
diff.IsIncomplete = true
|
||||
io.Copy(ioutil.Discard, reader)
|
||||
break
|
||||
}
|
||||
curFileLinesCount = 0
|
||||
|
||||
// Check file diff type and submodule.
|
||||
CHECK_TYPE:
|
||||
for {
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
isEOF = true
|
||||
} else {
|
||||
done <- fmt.Errorf("ReadString: %v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(line, "new file"):
|
||||
curFile.Type = DIFF_FILE_ADD
|
||||
curFile.IsCreated = true
|
||||
curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
|
||||
case strings.HasPrefix(line, "deleted"):
|
||||
curFile.Type = DIFF_FILE_DEL
|
||||
curFile.IsDeleted = true
|
||||
curFile.IsSubmodule = strings.HasSuffix(line, " 160000\n")
|
||||
case strings.HasPrefix(line, "index"):
|
||||
if curFile.IsDeleted {
|
||||
curFile.Index = line[6:46]
|
||||
} else if len(line) >= 88 {
|
||||
curFile.Index = line[49:88]
|
||||
} else {
|
||||
curFile.Index = curFile.Name
|
||||
}
|
||||
break CHECK_TYPE
|
||||
case strings.HasPrefix(line, "similarity index 100%"):
|
||||
curFile.Type = DIFF_FILE_RENAME
|
||||
curFile.IsRenamed = true
|
||||
curFile.OldName = curFile.Name
|
||||
curFile.Name = b
|
||||
curFile.Index = b
|
||||
break CHECK_TYPE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done <- nil
|
||||
return diff
|
||||
}
|
||||
|
||||
// GetDiffRange returns a parsed diff object between given commits.
|
||||
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
repo, err := OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commit, err := repo.GetCommit(afterCommitID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := NewCommand()
|
||||
if len(beforeCommitID) == 0 {
|
||||
// First commit of repository
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd.AddArguments("show", "--full-index", afterCommitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd.AddArguments("diff", "--full-index", "-M", c.ID.String(), afterCommitID)
|
||||
}
|
||||
} else {
|
||||
cmd.AddArguments("diff", "--full-index", "-M", beforeCommitID, afterCommitID)
|
||||
}
|
||||
|
||||
stdout, w := io.Pipe()
|
||||
done := make(chan error)
|
||||
var diff *Diff
|
||||
go func() {
|
||||
diff = ParsePatch(done, maxLines, maxLineCharacteres, maxFiles, stdout)
|
||||
}()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
err = cmd.RunInDirTimeoutPipeline(2*time.Minute, repoPath, w, stderr)
|
||||
w.Close() // Close writer to exit parsing goroutine
|
||||
if err != nil {
|
||||
return nil, concatenateError(err, stderr.String())
|
||||
}
|
||||
|
||||
return diff, <-done
|
||||
}
|
||||
|
||||
// RawDiffType represents the type of raw diff format.
|
||||
type RawDiffType string
|
||||
|
||||
const (
|
||||
RAW_DIFF_NORMAL RawDiffType = "diff"
|
||||
RAW_DIFF_PATCH RawDiffType = "patch"
|
||||
)
|
||||
|
||||
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
|
||||
func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error {
|
||||
repo, err := OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
commit, err := repo.GetCommit(commitID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := NewCommand()
|
||||
switch diffType {
|
||||
case RAW_DIFF_NORMAL:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd.AddArguments("show", commitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd.AddArguments("diff", "-M", c.ID.String(), commitID)
|
||||
}
|
||||
case RAW_DIFF_PATCH:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root", commitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
query := fmt.Sprintf("%s...%s", commitID, c.ID.String())
|
||||
cmd.AddArguments("format-patch", "--no-signature", "--stdout", query)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid diffType: %s", diffType)
|
||||
}
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
if err = cmd.RunInDirPipeline(repoPath, writer, stderr); err != nil {
|
||||
return concatenateError(err, stderr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDiffCommit returns a parsed diff object of given commit.
|
||||
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
|
||||
}
|
|
@ -159,10 +159,10 @@
|
|||
"revisionTime": "2016-08-10T03:50:02Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "WVDLV+YcA77fg1kgddSinycf3Do=",
|
||||
"checksumSHA1": "iZ3qMzzA24U3eqGifUZa6kFuCqk=",
|
||||
"path": "github.com/gogits/git-module",
|
||||
"revision": "f4026b57acf35d799d5dec95066f3a10c572604e",
|
||||
"revisionTime": "2017-04-03T19:07:43Z"
|
||||
"revision": "5dba4f883034198eabac620b73aabf5488fcd821",
|
||||
"revisionTime": "2017-04-04T05:34:13Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "D2kVXl0QpIw6t3891Sl7IM9wL+w=",
|
||||
|
|
Loading…
Reference in New Issue