}
// GetTailSection creates a fake DiffLineSection if the last section is not the end of the file
-func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommitID, rightCommitID string) *DiffSection {
+func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommit, rightCommit *git.Commit) *DiffSection {
if len(diffFile.Sections) == 0 || diffFile.Type != DiffFileChange || diffFile.IsBin || diffFile.IsLFSFile {
return nil
}
- leftCommit, err := gitRepo.GetCommit(leftCommitID)
- if err != nil {
- return nil
- }
- rightCommit, err := gitRepo.GetCommit(rightCommitID)
- if err != nil {
- return nil
- }
+
lastSection := diffFile.Sections[len(diffFile.Sections)-1]
lastLine := lastSection.Lines[len(lastSection.Lines)-1]
leftLineCount := getCommitFileLineCount(leftCommit, diffFile.Name)
lastFile := createDiffFile(diff, line)
diff.End = lastFile.Name
diff.IsIncomplete = true
- _, err := io.Copy(io.Discard, reader)
- if err != nil {
- // By the definition of io.Copy this never returns io.EOF
- return diff, fmt.Errorf("error during io.Copy: %w", err)
- }
break parsingLoop
}
MaxFiles int
WhitespaceBehavior git.TrustedCmdArgs
DirectComparison bool
+ FileOnly bool
}
// GetDiff builds a Diff between two commits of a repository.
func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
repoPath := gitRepo.Path
+ var beforeCommit *git.Commit
commit, err := gitRepo.GetCommit(opts.AfterCommitID)
if err != nil {
return nil, err
}
- cmdDiff := git.NewCommand(gitRepo.Ctx)
+ cmdCtx, cmdCancel := context.WithCancel(ctx)
+ defer cmdCancel()
+
+ cmdDiff := git.NewCommand(cmdCtx)
objectFormat, err := gitRepo.GetObjectFormat()
if err != nil {
return nil, err
AddArguments(opts.WhitespaceBehavior...).
AddDynamicArguments(actualBeforeCommitID, opts.AfterCommitID)
opts.BeforeCommitID = actualBeforeCommitID
+
+ var err error
+ beforeCommit, err = gitRepo.GetCommit(opts.BeforeCommitID)
+ if err != nil {
+ return nil, err
+ }
}
// In git 2.31, git diff learned --skip-to which we can use to shortcut skip to file
Dir: repoPath,
Stdout: writer,
Stderr: stderr,
- }); err != nil {
+ }); err != nil && err.Error() != "signal: killed" {
log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String())
}
_ = writer.Close()
}()
- diff, err := ParsePatch(ctx, opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, reader, parsePatchSkipToFile)
+ diff, err := ParsePatch(cmdCtx, opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, reader, parsePatchSkipToFile)
+ // Ensure the git process is killed if it didn't exit already
+ cmdCancel()
if err != nil {
return nil, fmt.Errorf("unable to ParsePatch: %w", err)
}
}
diffFile.IsGenerated = isGenerated.Value()
- tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID)
+ tailSection := diffFile.GetTailSection(gitRepo, beforeCommit, commit)
if tailSection != nil {
diffFile.Sections = append(diffFile.Sections, tailSection)
}
}
- separator := "..."
- if opts.DirectComparison {
- separator = ".."
+ if opts.FileOnly {
+ return diff, nil
}
- diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
- if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String() {
- diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID}
- }
- diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
- 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...
- diffPaths = []string{opts.BeforeCommitID, opts.AfterCommitID}
- diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
- }
+ stats, err := GetPullDiffStats(gitRepo, opts)
if err != nil {
return nil, err
}
+ diff.NumFiles, diff.TotalAddition, diff.TotalDeletion = stats.NumFiles, stats.TotalAddition, stats.TotalDeletion
+
return diff, nil
}
type PullDiffStats struct {
- TotalAddition, TotalDeletion int
+ NumFiles, TotalAddition, TotalDeletion int
}
// GetPullDiffStats
diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID}
}
- _, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
+ diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
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...
diffPaths = []string{opts.BeforeCommitID, opts.AfterCommitID}
- _, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
+ diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
}
if err != nil {
return nil, err
Stderr: stderr,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
+ defer cancel()
diff, finalErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
if finalErr != nil {
log.Error("ParsePatch: %v", finalErr)
log.Error("Unable to ParsePatch in temporary repo %s (%s). Error: %v", t.repo.FullName(), t.basePath, finalErr)
return nil, finalErr
}
- log.Error("Unable to run diff-index pipeline in temporary repo %s (%s). Error: %v\nStderr: %s",
- t.repo.FullName(), t.basePath, err, stderr)
- return nil, fmt.Errorf("Unable to run diff-index pipeline in temporary repo %s. Error: %w\nStderr: %s",
- t.repo.FullName(), err, stderr)
+
+ // If the process exited early, don't error
+ if err != context.Canceled {
+ log.Error("Unable to run diff-index pipeline in temporary repo %s (%s). Error: %v\nStderr: %s",
+ t.repo.FullName(), t.basePath, err, stderr)
+ return nil, fmt.Errorf("Unable to run diff-index pipeline in temporary repo %s. Error: %w\nStderr: %s",
+ t.repo.FullName(), err, stderr)
+ }
}
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.ctx, t.basePath, git.TrustedCmdArgs{"--cached"}, "HEAD")