2014-04-13 05:35:36 +04:00
// 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"
2014-12-09 10:18:25 +03:00
"bytes"
2014-06-19 09:08:03 +04:00
"fmt"
2016-01-09 09:51:17 +03:00
"html"
"html/template"
2014-04-13 05:35:36 +04:00
"io"
2015-12-14 17:38:21 +03:00
"io/ioutil"
2014-04-13 05:35:36 +04:00
"os"
"os/exec"
"strings"
2015-12-02 09:10:13 +03:00
"github.com/Unknwon/com"
2016-11-03 15:29:56 +03:00
"github.com/go-gitea/gitea/modules/base"
"github.com/go-gitea/gitea/modules/log"
"github.com/go-gitea/gitea/modules/process"
"github.com/go-gitea/gitea/modules/setting"
"github.com/go-gitea/gitea/modules/template/highlight"
2016-11-06 16:18:34 +03:00
"github.com/go-gitea/git"
2016-11-05 19:56:35 +03:00
"github.com/sergi/go-diff/diffmatchpatch"
"golang.org/x/net/html/charset"
"golang.org/x/text/transform"
2014-04-13 05:35:36 +04:00
)
2016-01-06 23:00:40 +03:00
type DiffLineType uint8
2014-04-13 05:35:36 +04:00
const (
2016-01-07 16:27:35 +03:00
DIFF_LINE_PLAIN DiffLineType = iota + 1
DIFF_LINE_ADD
DIFF_LINE_DEL
DIFF_LINE_SECTION
2014-04-13 05:35:36 +04:00
)
2016-01-06 23:00:40 +03:00
type DiffFileType uint8
2014-04-13 05:35:36 +04:00
const (
2016-01-07 16:27:35 +03:00
DIFF_FILE_ADD DiffFileType = iota + 1
DIFF_FILE_CHANGE
DIFF_FILE_DEL
DIFF_FILE_RENAME
2014-04-13 05:35:36 +04:00
)
type DiffLine struct {
2016-06-29 18:11:00 +03:00
LeftIdx int
RightIdx int
Type DiffLineType
Content string
2014-04-13 05:35:36 +04:00
}
2016-01-06 23:00:40 +03:00
func ( d * DiffLine ) GetType ( ) int {
return int ( d . Type )
2014-04-13 05:35:36 +04:00
}
type DiffSection struct {
Name string
Lines [ ] * DiffLine
}
2016-01-09 09:51:17 +03:00
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 {
2016-08-07 19:49:47 +03:00
buf := bytes . NewBuffer ( nil )
2016-08-16 17:33:53 +03:00
// Reproduce signs which are cutted for inline diff before.
switch lineType {
case DIFF_LINE_ADD :
buf . WriteByte ( '+' )
case DIFF_LINE_DEL :
buf . WriteByte ( '-' )
}
2016-01-09 09:51:17 +03:00
for i := range diffs {
2016-08-07 19:49:47 +03:00
switch {
case diffs [ i ] . Type == diffmatchpatch . DiffInsert && lineType == DIFF_LINE_ADD :
2016-01-09 09:51:17 +03:00
buf . Write ( addedCodePrefix )
buf . WriteString ( html . EscapeString ( diffs [ i ] . Text ) )
buf . Write ( codeTagSuffix )
2016-08-07 19:49:47 +03:00
case diffs [ i ] . Type == diffmatchpatch . DiffDelete && lineType == DIFF_LINE_DEL :
2016-01-09 09:51:17 +03:00
buf . Write ( removedCodePrefix )
buf . WriteString ( html . EscapeString ( diffs [ i ] . Text ) )
buf . Write ( codeTagSuffix )
2016-08-07 19:49:47 +03:00
case diffs [ i ] . Type == diffmatchpatch . DiffEqual :
2016-01-09 09:51:17 +03:00
buf . WriteString ( html . EscapeString ( diffs [ i ] . Text ) )
2016-01-04 00:26:46 +03:00
}
}
2016-01-09 09:51:17 +03:00
return template . HTML ( buf . Bytes ( ) )
2016-01-04 00:26:46 +03:00
}
2016-01-08 15:50:25 +03:00
// get an specific line by type (add or del) and file line number
func ( diffSection * DiffSection ) GetLine ( lineType DiffLineType , idx int ) * DiffLine {
2016-08-07 19:49:47 +03:00
var (
difference = 0
addCount = 0
delCount = 0
matchDiffLine * DiffLine
)
LOOP :
2016-01-08 15:50:25 +03:00
for _ , diffLine := range diffSection . Lines {
2016-08-07 19:49:47 +03:00
switch diffLine . Type {
case DIFF_LINE_ADD :
addCount ++
case DIFF_LINE_DEL :
delCount ++
default :
if matchDiffLine != nil {
break LOOP
}
2016-01-08 15:50:25 +03:00
difference = diffLine . RightIdx - diffLine . LeftIdx
2016-08-07 19:49:47 +03:00
addCount = 0
delCount = 0
2016-01-04 00:26:46 +03:00
}
2016-08-07 19:49:47 +03:00
switch lineType {
case DIFF_LINE_DEL :
2016-01-09 09:51:17 +03:00
if diffLine . RightIdx == 0 && diffLine . LeftIdx == idx - difference {
2016-08-07 19:49:47 +03:00
matchDiffLine = diffLine
2016-01-08 15:50:25 +03:00
}
2016-08-07 19:49:47 +03:00
case DIFF_LINE_ADD :
2016-01-09 09:51:17 +03:00
if diffLine . LeftIdx == 0 && diffLine . RightIdx == idx + difference {
2016-08-07 19:49:47 +03:00
matchDiffLine = diffLine
2016-01-04 00:26:46 +03:00
}
}
}
2016-08-07 19:49:47 +03:00
if addCount == delCount {
return matchDiffLine
}
2016-01-04 00:26:46 +03:00
return nil
}
2016-08-07 19:49:47 +03:00
var diffMatchPatch = diffmatchpatch . New ( )
func init ( ) {
diffMatchPatch . DiffEditCost = 100
}
2016-01-27 23:54:08 +03:00
// computes inline diff for the given line
func ( diffSection * DiffSection ) GetComputedInlineDiffFor ( diffLine * DiffLine ) template . HTML {
2016-08-07 19:49:47 +03:00
if setting . Git . DisableDiffHighlight {
2016-01-27 23:54:08 +03:00
return template . HTML ( html . EscapeString ( diffLine . Content [ 1 : ] ) )
}
2016-08-07 19:49:47 +03:00
var (
compareDiffLine * DiffLine
diff1 string
diff2 string
)
2016-01-04 00:26:46 +03:00
2016-01-27 23:54:08 +03:00
// try to find equivalent diff line. ignore, otherwise
2016-08-07 19:49:47 +03:00
switch diffLine . Type {
case DIFF_LINE_ADD :
2016-01-27 23:54:08 +03:00
compareDiffLine = diffSection . GetLine ( DIFF_LINE_DEL , diffLine . RightIdx )
if compareDiffLine == nil {
2016-08-16 17:37:28 +03:00
return template . HTML ( html . EscapeString ( diffLine . Content ) )
2016-01-27 23:54:08 +03:00
}
diff1 = compareDiffLine . Content
diff2 = diffLine . Content
2016-08-07 19:49:47 +03:00
case DIFF_LINE_DEL :
2016-01-27 23:54:08 +03:00
compareDiffLine = diffSection . GetLine ( DIFF_LINE_ADD , diffLine . LeftIdx )
if compareDiffLine == nil {
2016-08-16 17:37:28 +03:00
return template . HTML ( html . EscapeString ( diffLine . Content ) )
2016-01-04 00:26:46 +03:00
}
2016-01-27 23:54:08 +03:00
diff1 = diffLine . Content
diff2 = compareDiffLine . Content
2016-08-07 19:49:47 +03:00
default :
2016-08-16 17:33:53 +03:00
return template . HTML ( html . EscapeString ( diffLine . Content ) )
2016-01-27 23:54:08 +03:00
}
2016-01-04 00:26:46 +03:00
2016-08-07 19:49:47 +03:00
diffRecord := diffMatchPatch . DiffMain ( diff1 [ 1 : ] , diff2 [ 1 : ] , true )
diffRecord = diffMatchPatch . DiffCleanupEfficiency ( diffRecord )
2016-01-04 00:26:46 +03:00
2016-01-27 23:54:08 +03:00
return diffToHTML ( diffRecord , diffLine . Type )
2016-01-04 00:26:46 +03:00
}
2014-04-13 05:35:36 +04:00
type DiffFile struct {
Name string
2015-11-03 03:55:24 +03:00
OldName string
2014-05-13 20:40:32 +04:00
Index int
2014-04-13 05:35:36 +04:00
Addition , Deletion int
2016-01-06 23:00:40 +03:00
Type DiffFileType
2015-02-06 12:02:32 +03:00
IsCreated bool
IsDeleted bool
2014-04-16 04:01:20 +04:00
IsBin bool
2015-11-03 03:55:24 +03:00
IsRenamed bool
2016-07-22 21:18:56 +03:00
IsSubmodule bool
2014-04-13 05:35:36 +04:00
Sections [ ] * DiffSection
2016-06-29 18:11:00 +03:00
IsIncomplete bool
2014-04-13 05:35:36 +04:00
}
2016-01-06 23:00:40 +03:00
func ( diffFile * DiffFile ) GetType ( ) int {
return int ( diffFile . Type )
}
2016-01-31 19:19:02 +03:00
func ( diffFile * DiffFile ) GetHighlightClass ( ) string {
2016-02-22 00:45:24 +03:00
return highlight . FileNameToHighlightClass ( diffFile . Name )
2016-01-31 19:19:02 +03:00
}
2014-04-13 05:35:36 +04:00
type Diff struct {
TotalAddition , TotalDeletion int
Files [ ] * DiffFile
2016-06-29 18:11:00 +03:00
IsIncomplete bool
2014-04-13 05:35:36 +04:00
}
func ( diff * Diff ) NumFiles ( ) int {
return len ( diff . Files )
}
const DIFF_HEAD = "diff --git "
2016-07-30 18:39:58 +03:00
// TODO: move this function to gogits/git-module
2016-06-29 18:11:00 +03:00
func ParsePatch ( maxLines , maxLineCharacteres , maxFiles int , reader io . Reader ) ( * Diff , error ) {
2014-04-13 05:35:36 +04:00
var (
2015-12-02 09:10:13 +03:00
diff = & Diff { Files : make ( [ ] * DiffFile , 0 ) }
2014-04-13 05:35:36 +04:00
curFile * DiffFile
curSection = & DiffSection {
Lines : make ( [ ] * DiffLine , 0 , 10 ) ,
}
leftLine , rightLine int
2015-12-02 09:10:13 +03:00
lineCount int
2016-06-29 18:11:00 +03:00
curFileLinesCount int
2014-04-13 05:35:36 +04:00
)
2015-12-02 09:10:13 +03:00
input := bufio . NewReader ( reader )
isEOF := false
2016-06-29 18:11:00 +03:00
for ! isEOF {
2015-12-02 09:10:13 +03:00
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 ]
2014-04-13 05:35:36 +04:00
}
2016-06-29 18:11:00 +03:00
if strings . HasPrefix ( line , "+++ " ) || strings . HasPrefix ( line , "--- " ) || len ( line ) == 0 {
2014-09-17 08:03:03 +04:00
continue
}
2016-06-29 18:11:00 +03:00
curFileLinesCount ++
2015-12-02 09:10:13 +03:00
lineCount ++
2014-04-13 05:35:36 +04:00
2014-09-17 08:03:03 +04:00
// Diff data too large, we only show the first about maxlines lines
2016-06-29 18:11:00 +03:00
if curFileLinesCount >= maxLines || len ( line ) >= maxLineCharacteres {
curFile . IsIncomplete = true
2014-04-13 05:35:36 +04:00
}
2014-04-16 04:01:20 +04:00
switch {
case line [ 0 ] == ' ' :
2014-04-13 05:35:36 +04:00
diffLine := & DiffLine { Type : DIFF_LINE_PLAIN , Content : line , LeftIdx : leftLine , RightIdx : rightLine }
leftLine ++
rightLine ++
curSection . Lines = append ( curSection . Lines , diffLine )
continue
2014-04-16 04:01:20 +04:00
case line [ 0 ] == '@' :
2014-04-13 05:35:36 +04:00
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.
2015-07-29 17:55:01 +03:00
ranges := strings . Split ( ss [ 1 ] [ 1 : ] , " " )
2014-07-26 08:24:27 +04:00
leftLine , _ = com . StrTo ( strings . Split ( ranges [ 0 ] , "," ) [ 0 ] [ 1 : ] ) . Int ( )
2015-07-29 17:55:01 +03:00
if len ( ranges ) > 1 {
rightLine , _ = com . StrTo ( strings . Split ( ranges [ 1 ] , "," ) [ 0 ] ) . Int ( )
} else {
log . Warn ( "Parse line number failed: %v" , line )
rightLine = leftLine
}
2014-04-13 05:35:36 +04:00
continue
2014-04-16 04:01:20 +04:00
case line [ 0 ] == '+' :
2014-04-13 05:35:36 +04:00
curFile . Addition ++
diff . TotalAddition ++
diffLine := & DiffLine { Type : DIFF_LINE_ADD , Content : line , RightIdx : rightLine }
rightLine ++
curSection . Lines = append ( curSection . Lines , diffLine )
continue
2014-04-16 04:01:20 +04:00
case line [ 0 ] == '-' :
2014-04-13 05:35:36 +04:00
curFile . Deletion ++
diff . TotalDeletion ++
diffLine := & DiffLine { Type : DIFF_LINE_DEL , Content : line , LeftIdx : leftLine }
if leftLine > 0 {
leftLine ++
}
curSection . Lines = append ( curSection . Lines , diffLine )
2014-04-16 04:01:20 +04:00
case strings . HasPrefix ( line , "Binary" ) :
curFile . IsBin = true
2014-04-13 05:35:36 +04:00
continue
}
// Get new file.
if strings . HasPrefix ( line , DIFF_HEAD ) {
2015-11-03 03:55:24 +03:00
middle := - 1
2015-11-20 09:18:50 +03:00
// 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 ) ] == '"'
2015-11-03 03:55:24 +03:00
if hasQuote {
middle = strings . Index ( line , ` "b/ ` )
} else {
middle = strings . Index ( line , " b/" )
}
2014-04-13 05:35:36 +04:00
2015-11-03 03:55:24 +03:00
beg := len ( DIFF_HEAD )
a := line [ beg + 2 : middle ]
b := line [ middle + 3 : ]
if hasQuote {
2015-11-20 09:18:50 +03:00
a = string ( git . UnescapeChars ( [ ] byte ( a [ 1 : len ( a ) - 1 ] ) ) )
b = string ( git . UnescapeChars ( [ ] byte ( b [ 1 : len ( b ) - 1 ] ) ) )
2015-08-20 11:08:26 +03:00
}
2014-04-13 05:35:36 +04:00
curFile = & DiffFile {
2015-11-03 03:55:24 +03:00
Name : a ,
2014-05-13 20:40:32 +04:00
Index : len ( diff . Files ) + 1 ,
2014-04-13 05:35:36 +04:00
Type : DIFF_FILE_CHANGE ,
Sections : make ( [ ] * DiffSection , 0 , 10 ) ,
}
diff . Files = append ( diff . Files , curFile )
2016-06-29 18:11:00 +03:00
if len ( diff . Files ) >= maxFiles {
diff . IsIncomplete = true
io . Copy ( ioutil . Discard , reader )
break
}
curFileLinesCount = 0
2014-04-13 05:35:36 +04:00
2016-07-22 21:18:56 +03:00
// Check file diff type and is submodule.
2015-12-02 09:10:13 +03:00
for {
line , err := input . ReadString ( '\n' )
if err != nil {
if err == io . EOF {
isEOF = true
} else {
return nil , fmt . Errorf ( "ReadString: %v" , err )
}
}
2014-04-13 05:35:36 +04:00
switch {
2015-12-02 09:10:13 +03:00
case strings . HasPrefix ( line , "new file" ) :
2014-04-13 05:35:36 +04:00
curFile . Type = DIFF_FILE_ADD
2015-02-06 12:02:32 +03:00
curFile . IsCreated = true
2015-12-02 09:10:13 +03:00
case strings . HasPrefix ( line , "deleted" ) :
2014-04-13 05:35:36 +04:00
curFile . Type = DIFF_FILE_DEL
2015-02-06 12:02:32 +03:00
curFile . IsDeleted = true
2015-12-02 09:10:13 +03:00
case strings . HasPrefix ( line , "index" ) :
2014-04-13 05:35:36 +04:00
curFile . Type = DIFF_FILE_CHANGE
2015-12-02 09:10:13 +03:00
case strings . HasPrefix ( line , "similarity index 100%" ) :
2015-11-03 03:55:24 +03:00
curFile . Type = DIFF_FILE_RENAME
curFile . IsRenamed = true
curFile . OldName = curFile . Name
curFile . Name = b
2014-04-13 05:35:36 +04:00
}
if curFile . Type > 0 {
2016-07-22 21:18:56 +03:00
if strings . HasSuffix ( line , " 160000\n" ) {
curFile . IsSubmodule = true
}
2014-04-13 05:35:36 +04:00
break
}
}
}
}
2015-12-02 09:10:13 +03:00
// FIXME: detect encoding while parsing.
var buf bytes . Buffer
2015-07-29 17:55:01 +03:00
for _ , f := range diff . Files {
buf . Reset ( )
for _ , sec := range f . Sections {
for _ , l := range sec . Lines {
buf . WriteString ( l . Content )
buf . WriteString ( "\n" )
}
}
2016-01-01 06:13:47 +03:00
charsetLabel , err := base . DetectEncoding ( buf . Bytes ( ) )
if charsetLabel != "UTF-8" && err == nil {
2015-07-29 17:55:01 +03:00
encoding , _ := charset . Lookup ( charsetLabel )
if encoding != nil {
d := encoding . NewDecoder ( )
2014-12-09 10:18:25 +03:00
for _ , sec := range f . Sections {
for _ , l := range sec . Lines {
2014-12-22 12:01:52 +03:00
if c , _ , err := transform . String ( d , l . Content ) ; err == nil {
l . Content = c
}
2014-12-09 10:18:25 +03:00
}
}
}
}
}
2014-04-13 05:35:36 +04:00
return diff , nil
}
2016-07-30 18:39:58 +03:00
func GetDiffRange ( repoPath , beforeCommitID , afterCommitID string , maxLines , maxLineCharacteres , maxFiles int ) ( * Diff , error ) {
gitRepo , err := git . OpenRepository ( repoPath )
2014-04-13 05:35:36 +04:00
if err != nil {
return nil , err
}
2016-07-30 18:39:58 +03:00
commit , err := gitRepo . GetCommit ( afterCommitID )
2014-04-13 05:35:36 +04:00
if err != nil {
return nil , err
}
2014-05-29 06:15:15 +04:00
var cmd * exec . Cmd
2014-08-26 16:20:18 +04:00
// if "after" commit given
2015-12-02 09:10:13 +03:00
if len ( beforeCommitID ) == 0 {
2014-08-26 16:20:18 +04:00
// First commit of repository.
if commit . ParentCount ( ) == 0 {
2015-12-02 09:10:13 +03:00
cmd = exec . Command ( "git" , "show" , afterCommitID )
2014-08-26 16:20:18 +04:00
} else {
c , _ := commit . Parent ( 0 )
2015-12-02 09:10:13 +03:00
cmd = exec . Command ( "git" , "diff" , "-M" , c . ID . String ( ) , afterCommitID )
2014-08-26 16:20:18 +04:00
}
2014-05-29 06:15:15 +04:00
} else {
2015-12-02 09:10:13 +03:00
cmd = exec . Command ( "git" , "diff" , "-M" , beforeCommitID , afterCommitID )
2014-04-13 05:35:36 +04:00
}
2014-05-29 06:15:15 +04:00
cmd . Dir = repoPath
cmd . Stderr = os . Stderr
2014-07-07 01:32:36 +04:00
2015-12-02 09:10:13 +03:00
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 )
}
2014-07-07 01:32:36 +04:00
2016-08-15 09:02:14 +03:00
pid := process . Add ( fmt . Sprintf ( "GetDiffRange [repo_path: %s]" , repoPath ) , cmd )
2015-12-02 09:10:13 +03:00
defer process . Remove ( pid )
2016-06-29 18:11:00 +03:00
diff , err := ParsePatch ( maxLines , maxLineCharacteres , maxFiles , stdout )
2015-12-02 09:10:13 +03:00
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
2014-04-13 05:35:36 +04:00
}
2014-08-26 16:20:18 +04:00
2016-07-30 18:39:58 +03:00
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 {
2016-07-30 18:02:22 +03:00
repo , err := git . OpenRepository ( repoPath )
if err != nil {
2016-07-30 18:39:58 +03:00
return fmt . Errorf ( "OpenRepository: %v" , err )
2016-07-30 18:02:22 +03:00
}
commit , err := repo . GetCommit ( commitID )
if err != nil {
2016-07-30 18:39:58 +03:00
return fmt . Errorf ( "GetCommit: %v" , err )
2016-07-30 18:02:22 +03:00
}
var cmd * exec . Cmd
switch diffType {
2016-07-30 18:39:58 +03:00
case RAW_DIFF_NORMAL :
2016-07-30 18:02:22 +03:00
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 )
}
2016-07-30 18:39:58 +03:00
case RAW_DIFF_PATCH :
2016-07-30 18:02:22 +03:00
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 :
2016-07-30 18:39:58 +03:00
return fmt . Errorf ( "invalid diffType: %s" , diffType )
2016-07-30 18:02:22 +03:00
}
stderr := new ( bytes . Buffer )
cmd . Dir = repoPath
2016-07-30 18:39:58 +03:00
cmd . Stdout = writer
2016-07-30 18:02:22 +03:00
cmd . Stderr = stderr
2016-07-30 18:39:58 +03:00
if err = cmd . Run ( ) ; err != nil {
return fmt . Errorf ( "Run: %v - %s" , err , stderr )
2016-07-30 18:02:22 +03:00
}
2016-07-30 18:39:58 +03:00
return nil
2016-07-30 18:02:22 +03:00
}
func GetDiffCommit ( repoPath , commitID string , maxLines , maxLineCharacteres , maxFiles int ) ( * Diff , error ) {
return GetDiffRange ( repoPath , "" , commitID , maxLines , maxLineCharacteres , maxFiles )
2014-08-26 16:20:18 +04:00
}