@ -695,6 +695,7 @@ const cmdDiffHead = "diff --git "
// ParsePatch builds a Diff object from a io.Reader and some parameters.
func ParsePatch ( maxLines , maxLineCharacters , maxFiles int , reader io . Reader , skipToFile string ) ( * Diff , error ) {
log . Debug ( "ParsePatch(%d, %d, %d, ..., %s)" , maxLines , maxLineCharacters , maxFiles , skipToFile )
var curFile * DiffFile
skipping := skipToFile != ""
@ -726,7 +727,7 @@ parsingLoop:
return diff , fmt . Errorf ( "invalid first file line: %s" , line )
}
if len ( diff . Files ) >= maxFiles {
if maxFiles > - 1 && len ( diff . Files ) >= maxFiles {
lastFile := createDiffFile ( diff , line )
diff . End = lastFile . Name
diff . IsIncomplete = true
@ -1038,7 +1039,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
switch lineBytes [ 0 ] {
case '@' :
if curFileLinesCount >= maxLines {
if maxLines > - 1 && curFileLinesCount >= maxLines {
curFile . IsIncomplete = true
continue
}
@ -1075,7 +1076,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
rightLine = lineSectionInfo . RightIdx
continue
case '\\' :
if curFileLinesCount >= maxLines {
if maxLines > - 1 && curFileLinesCount >= maxLines {
curFile . IsIncomplete = true
continue
}
@ -1090,7 +1091,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
case '+' :
curFileLinesCount ++
curFile . Addition ++
if curFileLinesCount >= maxLines {
if maxLines > - 1 && curFileLinesCount >= maxLines {
curFile . IsIncomplete = true
continue
}
@ -1114,7 +1115,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
case '-' :
curFileLinesCount ++
curFile . Deletion ++
if curFileLinesCount >= maxLines {
if maxLines > - 1 && curFileLinesCount >= maxLines {
curFile . IsIncomplete = true
continue
}
@ -1134,7 +1135,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
curSection . Lines = append ( curSection . Lines , diffLine )
case ' ' :
curFileLinesCount ++
if curFileLinesCount >= maxLines {
if maxLines > - 1 && curFileLinesCount >= maxLines {
curFile . IsIncomplete = true
continue
}
@ -1278,13 +1279,25 @@ func readFileName(rd *strings.Reader) (string, bool) {
return name [ 2 : ] , ambiguity
}
// GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository.
// DiffOptions represents the options for a DiffRange
type DiffOptions struct {
BeforeCommitID string
AfterCommitID string
SkipTo string
MaxLines int
MaxLineCharacters int
MaxFiles int
WhitespaceBehavior string
DirectComparison bool
}
// GetDiff 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 ( gitRepo * git . Repository , beforeCommitID , afterCommitID , skipTo string , maxLines , maxLineCharacters , maxFiles int , whitespaceBehavior string , directComparison bool ) ( * Diff , error ) {
func GetDiff ( gitRepo * git . Repository , opts * DiffOptions , files ... string ) ( * Diff , error ) {
repoPath := gitRepo . Path
commit , err := gitRepo . GetCommit ( afterCommitID )
commit , err := gitRepo . GetCommit ( opts . A fterCommitID)
if err != nil {
return nil , err
}
@ -1293,45 +1306,54 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
defer cancel ( )
argsLength := 6
if len ( w hitespaceBehavior) > 0 {
if len ( opts . W hitespaceBehavior) > 0 {
argsLength ++
}
if len ( skipTo ) > 0 {
if len ( opt s. S kipTo) > 0 {
argsLength ++
}
if len ( files ) > 0 {
argsLength += len ( files ) + 1
}
diffArgs := make ( [ ] string , 0 , argsLength )
if ( len ( beforeCommitID ) == 0 || beforeCommitID == git . EmptySHA ) && commit . ParentCount ( ) == 0 {
if ( len ( opts . B eforeCommitID) == 0 || opts . B eforeCommitID == git . EmptySHA ) && commit . ParentCount ( ) == 0 {
diffArgs = append ( diffArgs , "diff" , "--src-prefix=\\a/" , "--dst-prefix=\\b/" , "-M" )
if len ( w hitespaceBehavior) != 0 {
diffArgs = append ( diffArgs , w hitespaceBehavior)
if len ( opts . W hitespaceBehavior) != 0 {
diffArgs = append ( diffArgs , opts . W hitespaceBehavior)
}
// append empty tree ref
diffArgs = append ( diffArgs , "4b825dc642cb6eb9a060e54bf8d69288fbee4904" )
diffArgs = append ( diffArgs , a fterCommitID)
diffArgs = append ( diffArgs , opts . A fterCommitID)
} else {
actualBeforeCommitID := b eforeCommitID
actualBeforeCommitID := opts . B eforeCommitID
if len ( actualBeforeCommitID ) == 0 {
parentCommit , _ := commit . Parent ( 0 )
actualBeforeCommitID = parentCommit . ID . String ( )
}
diffArgs = append ( diffArgs , "diff" , "--src-prefix=\\a/" , "--dst-prefix=\\b/" , "-M" )
if len ( w hitespaceBehavior) != 0 {
diffArgs = append ( diffArgs , w hitespaceBehavior)
if len ( opts . W hitespaceBehavior) != 0 {
diffArgs = append ( diffArgs , opts . W hitespaceBehavior)
}
diffArgs = append ( diffArgs , actualBeforeCommitID )
diffArgs = append ( diffArgs , a fterCommitID)
b eforeCommitID = actualBeforeCommitID
diffArgs = append ( diffArgs , opts . A fterCommitID)
opts . B eforeCommitID = actualBeforeCommitID
}
// In git 2.31, git diff learned --skip-to which we can use to shortcut skip to file
// so if we are using at least this version of git we don't have to tell ParsePatch to do
// the skipping for us
parsePatchSkipToFile := skipTo
if skipTo != "" && git . CheckGitVersionAtLeast ( "2.31" ) == nil {
diffArgs = append ( diffArgs , "--skip-to=" + skipTo )
parsePatchSkipToFile := opt s. S kipTo
if opt s. S kipTo != "" && git . CheckGitVersionAtLeast ( "2.31" ) == nil {
diffArgs = append ( diffArgs , "--skip-to=" + opt s. S kipTo)
parsePatchSkipToFile = ""
}
if len ( files ) > 0 {
diffArgs = append ( diffArgs , "--" )
diffArgs = append ( diffArgs , files ... )
}
cmd := exec . CommandContext ( ctx , git . GitExecutable , diffArgs ... )
cmd . Dir = repoPath
@ -1349,16 +1371,16 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
pid := process . GetManager ( ) . Add ( fmt . Sprintf ( "GetDiffRange [repo_path: %s]" , repoPath ) , cancel )
defer process . GetManager ( ) . Remove ( pid )
diff , err := ParsePatch ( maxLines , maxLineCharacters , m axFiles, stdout , parsePatchSkipToFile )
diff , err := ParsePatch ( opts . MaxLines , opts . MaxLineCharacters , opts . M axFiles, stdout , parsePatchSkipToFile )
if err != nil {
return nil , fmt . Errorf ( "unable to ParsePatch: %w" , err )
}
diff . Start = skipTo
diff . Start = opt s. S kipTo
var checker * git . CheckAttributeReader
if git . CheckGitVersionAtLeast ( "1.7.8" ) == nil {
indexFilename , worktree , deleteTemporaryFile , err := gitRepo . ReadTreeToTemporaryIndex ( a fterCommitID)
indexFilename , worktree , deleteTemporaryFile , err := gitRepo . ReadTreeToTemporaryIndex ( opts . A fterCommitID)
if err == nil {
defer deleteTemporaryFile ( )
@ -1370,12 +1392,12 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
}
ctx , cancel := context . WithCancel ( git . DefaultContext )
if err := checker . Init ( ctx ) ; err != nil {
log . Error ( "Unable to open checker for %s. Error: %v" , a fterCommitID, err )
log . Error ( "Unable to open checker for %s. Error: %v" , opts . A fterCommitID, err )
} else {
go func ( ) {
err := checker . Run ( )
if err != nil && err != ctx . Err ( ) {
log . Error ( "Unable to open checker for %s. Error: %v" , a fterCommitID, err )
log . Error ( "Unable to open checker for %s. Error: %v" , opts . A fterCommitID, err )
}
cancel ( )
} ( )
@ -1426,7 +1448,7 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
diffFile . IsGenerated = analyze . IsGenerated ( diffFile . Name )
}
tailSection := diffFile . GetTailSection ( gitRepo , b eforeCommitID, a fterCommitID)
tailSection := diffFile . GetTailSection ( gitRepo , opts . B eforeCommitID, opts . A fterCommitID)
if tailSection != nil {
diffFile . Sections = append ( diffFile . Sections , tailSection )
}
@ -1437,19 +1459,19 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
}
separator := "..."
if d irectComparison {
if opts . D irectComparison {
separator = ".."
}
shortstatArgs := [ ] string { b eforeCommitID + separator + a fterCommitID}
if len ( b eforeCommitID) == 0 || b eforeCommitID == git . EmptySHA {
shortstatArgs = [ ] string { git . EmptyTreeSHA , a fterCommitID}
shortstatArgs := [ ] string { opts . B eforeCommitID + separator + opts . A fterCommitID}
if len ( opts . B eforeCommitID) == 0 || opts . B eforeCommitID == git . EmptySHA {
shortstatArgs = [ ] string { git . EmptyTreeSHA , opts . A fterCommitID}
}
diff . NumFiles , diff . TotalAddition , diff . TotalDeletion , err = git . GetDiffShortStat ( repoPath , shortstatArgs ... )
if err != nil && strings . Contains ( err . Error ( ) , "no merge base" ) {
// git >= 2.28 now returns an error if base and head have become unrelated.
// previously it would return the results of git diff --shortstat base head so let's try that...
shortstatArgs = [ ] string { b eforeCommitID, a fterCommitID}
shortstatArgs = [ ] string { opts . B eforeCommitID, opts . A fterCommitID}
diff . NumFiles , diff . TotalAddition , diff . TotalDeletion , err = git . GetDiffShortStat ( repoPath , shortstatArgs ... )
}
if err != nil {
@ -1459,12 +1481,6 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
return diff , nil
}
// GetDiffCommitWithWhitespaceBehavior builds a Diff representing the given commitID.
// The whitespaceBehavior is either an empty string or a git flag
func GetDiffCommitWithWhitespaceBehavior ( gitRepo * git . Repository , commitID , skipTo string , maxLines , maxLineCharacters , maxFiles int , whitespaceBehavior string , directComparison bool ) ( * Diff , error ) {
return GetDiffRangeWithWhitespaceBehavior ( gitRepo , "" , commitID , skipTo , maxLines , maxLineCharacters , maxFiles , whitespaceBehavior , directComparison )
}
// CommentAsDiff returns c.Patch as *Diff
func CommentAsDiff ( c * models . Comment ) ( * Diff , error ) {
diff , err := ParsePatch ( setting . Git . MaxGitDiffLines ,