Browse Source

models/repo_diff: move core functions to gogits/git-module

Unknwon 7 years ago
parent
commit
55afc1ad21

+ 1 - 1
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

+ 59 - 386
models/git_diff.go

@@ -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
-}
-
-func (diff *Diff) NumFiles() int {
-	return len(diff.Files)
+	*git.Diff
+	Files []*DiffFile
 }
 
-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
-		}
+func NewDiff(gitDiff *git.Diff) *Diff {
+	diff := &Diff{
+		Diff:  gitDiff,
+		Files: make([]*DiffFile, gitDiff.NumFiles()),
+	}
 
-		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)
+	// FIXME: detect encoding while parsing.
+	var buf bytes.Buffer
+	for i := range gitDiff.Files {
+		buf.Reset()
 
-			// 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
+		diff.Files[i] = &DiffFile{
+			DiffFile: gitDiff.Files[i],
+			Sections: make([]*DiffSection, gitDiff.Files[i].NumSections()),
 		}
 
-		// 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])))
+		for j := range gitDiff.Files[i].Sections {
+			diff.Files[i].Sections[j] = &DiffSection{
+				DiffSection: gitDiff.Files[i].Sections[j],
 			}
 
-			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
-				}
-			}
-		}
-	}
-
-	// 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)
+			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
-}
-
-func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*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 "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)
-	}
+	return diff
+}
 
-	pid := process.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd)
-	defer process.Remove(pid)
+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)
+	}()
 
-	diff, err := ParsePatch(maxLines, maxLineCharacteres, maxFiles, stdout)
-	if err != nil {
+	if err := <-done; 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
+	return NewDiff(gitDiff), 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)
+func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
+	gitDiff, err := git.GetDiffRange(repoPath, beforeCommitID, afterCommitID, maxLines, maxLineCharacteres, maxFiles)
 	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)
+		return nil, fmt.Errorf("GetDiffRange: %v", err)
 	}
-
-	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
 }

+ 11 - 5
models/git_diff_test.go

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

+ 3 - 3
routers/repo/commit.go

@@ -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
templates/.VERSION

@@ -1 +1 @@
-0.11.1.0403
+0.11.2.0404

+ 4 - 4
templates/repo/diff/box.tmpl

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

+ 2 - 2
templates/repo/diff/section_unified.tmpl

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

+ 7 - 5
vendor/github.com/gogits/git-module/commit.go

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

+ 1 - 1
vendor/github.com/gogits/git-module/git.go

@@ -10,7 +10,7 @@ import (
 	"time"
 )
 
-const _VERSION = "0.5.1"
+const _VERSION = "0.6.0"
 
 func Version() string {
 	return _VERSION

+ 398 - 0
vendor/github.com/gogits/git-module/repo_diff.go

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

+ 3 - 3
vendor/vendor.json

@@ -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=",