// 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 != ""
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
switch lineBytes[0] {
case '@':
- if curFileLinesCount >= maxLines {
+ if maxLines > -1 && curFileLinesCount >= maxLines {
curFile.IsIncomplete = true
continue
}
rightLine = lineSectionInfo.RightIdx
continue
case '\\':
- if curFileLinesCount >= maxLines {
+ if maxLines > -1 && curFileLinesCount >= maxLines {
curFile.IsIncomplete = true
continue
}
case '+':
curFileLinesCount++
curFile.Addition++
- if curFileLinesCount >= maxLines {
+ if maxLines > -1 && curFileLinesCount >= maxLines {
curFile.IsIncomplete = true
continue
}
case '-':
curFileLinesCount++
curFile.Deletion++
- if curFileLinesCount >= maxLines {
+ if maxLines > -1 && curFileLinesCount >= maxLines {
curFile.IsIncomplete = true
continue
}
curSection.Lines = append(curSection.Lines, diffLine)
case ' ':
curFileLinesCount++
- if curFileLinesCount >= maxLines {
+ if maxLines > -1 && curFileLinesCount >= maxLines {
curFile.IsIncomplete = true
continue
}
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.AfterCommitID)
if err != nil {
return nil, err
}
defer cancel()
argsLength := 6
- if len(whitespaceBehavior) > 0 {
+ if len(opts.WhitespaceBehavior) > 0 {
argsLength++
}
- if len(skipTo) > 0 {
+ if len(opts.SkipTo) > 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.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 {
diffArgs = append(diffArgs, "diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M")
- if len(whitespaceBehavior) != 0 {
- diffArgs = append(diffArgs, whitespaceBehavior)
+ if len(opts.WhitespaceBehavior) != 0 {
+ diffArgs = append(diffArgs, opts.WhitespaceBehavior)
}
// append empty tree ref
diffArgs = append(diffArgs, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")
- diffArgs = append(diffArgs, afterCommitID)
+ diffArgs = append(diffArgs, opts.AfterCommitID)
} else {
- actualBeforeCommitID := beforeCommitID
+ actualBeforeCommitID := opts.BeforeCommitID
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(whitespaceBehavior) != 0 {
- diffArgs = append(diffArgs, whitespaceBehavior)
+ if len(opts.WhitespaceBehavior) != 0 {
+ diffArgs = append(diffArgs, opts.WhitespaceBehavior)
}
diffArgs = append(diffArgs, actualBeforeCommitID)
- diffArgs = append(diffArgs, afterCommitID)
- beforeCommitID = actualBeforeCommitID
+ diffArgs = append(diffArgs, opts.AfterCommitID)
+ opts.BeforeCommitID = 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 := opts.SkipTo
+ if opts.SkipTo != "" && git.CheckGitVersionAtLeast("2.31") == nil {
+ diffArgs = append(diffArgs, "--skip-to="+opts.SkipTo)
parsePatchSkipToFile = ""
}
+
+ if len(files) > 0 {
+ diffArgs = append(diffArgs, "--")
+ diffArgs = append(diffArgs, files...)
+ }
+
cmd := exec.CommandContext(ctx, git.GitExecutable, diffArgs...)
cmd.Dir = repoPath
pid := process.GetManager().Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cancel)
defer process.GetManager().Remove(pid)
- diff, err := ParsePatch(maxLines, maxLineCharacters, maxFiles, stdout, parsePatchSkipToFile)
+ diff, err := ParsePatch(opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, stdout, parsePatchSkipToFile)
if err != nil {
return nil, fmt.Errorf("unable to ParsePatch: %w", err)
}
- diff.Start = skipTo
+ diff.Start = opts.SkipTo
var checker *git.CheckAttributeReader
if git.CheckGitVersionAtLeast("1.7.8") == nil {
- indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(afterCommitID)
+ indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(opts.AfterCommitID)
if err == nil {
defer deleteTemporaryFile()
}
ctx, cancel := context.WithCancel(git.DefaultContext)
if err := checker.Init(ctx); err != nil {
- log.Error("Unable to open checker for %s. Error: %v", afterCommitID, err)
+ log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err)
} else {
go func() {
err := checker.Run()
if err != nil && err != ctx.Err() {
- log.Error("Unable to open checker for %s. Error: %v", afterCommitID, err)
+ log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err)
}
cancel()
}()
diffFile.IsGenerated = analyze.IsGenerated(diffFile.Name)
}
- tailSection := diffFile.GetTailSection(gitRepo, beforeCommitID, afterCommitID)
+ tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID)
if tailSection != nil {
diffFile.Sections = append(diffFile.Sections, tailSection)
}
}
separator := "..."
- if directComparison {
+ if opts.DirectComparison {
separator = ".."
}
- shortstatArgs := []string{beforeCommitID + separator + afterCommitID}
- if len(beforeCommitID) == 0 || beforeCommitID == git.EmptySHA {
- shortstatArgs = []string{git.EmptyTreeSHA, afterCommitID}
+ shortstatArgs := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
+ if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA {
+ shortstatArgs = []string{git.EmptyTreeSHA, opts.AfterCommitID}
}
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{beforeCommitID, afterCommitID}
+ shortstatArgs = []string{opts.BeforeCommitID, opts.AfterCommitID}
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(repoPath, shortstatArgs...)
}
if err != nil {
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,