in internal/platform/git/git_changes.go [113:212]
func parseDiff(diffPath string, repoRoot string, cwd string) (ChangedFiles, error) {
log.Debugf("Parsing diff - repo root: %s, cwd: %s", repoRoot, cwd)
var changes []HunkChange
diffFile, err := os.Open(diffPath)
if err != nil {
return ChangedFiles{}, fmt.Errorf("failed to open diff file %s: %w", diffPath, err)
}
defer func(diffFile *os.File) {
err := diffFile.Close()
if err != nil {
log.Errorf("failed to close diff file %s: %s", diffPath, err)
}
}(diffFile)
scanner := bufio.NewReader(diffFile)
var currentChange *HunkChange
var line string
// Regular expressions to match diff headers and hunks
reFilename := regexp.MustCompile(`^diff --git a/(.*) b/(.*)`)
reHunk := regexp.MustCompile(`^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@`)
for {
line, err = scanner.ReadString('\n')
if err == io.EOF || err != nil {
break
}
line = strings.TrimSpace(line)
if matches := reFilename.FindStringSubmatch(line); matches != nil {
if currentChange != nil {
changes = append(changes, *currentChange)
}
currentChange = &HunkChange{
FromPath: matches[1],
ToPath: matches[2],
Added: []*ChangedRegion{},
Deleted: []*ChangedRegion{},
}
continue
}
if matches := reHunk.FindStringSubmatch(line); matches != nil && currentChange != nil {
origLineStart := diffToInt(matches[1])
origCount := diffToInt(matches[2])
newLineStart := diffToInt(matches[3])
newCount := diffToInt(matches[4])
if origCount != 0 {
currentChange.Deleted = append(
currentChange.Deleted,
&ChangedRegion{FirstLine: origLineStart, Count: origCount},
)
}
if newCount != 0 {
currentChange.Added = append(
currentChange.Added,
&ChangedRegion{FirstLine: newLineStart, Count: newCount},
)
}
}
}
if currentChange != nil {
changes = append(changes, *currentChange)
}
if err != nil && err != io.EOF {
return ChangedFiles{}, err
}
files := make([]*ChangedFile, 0, len(changes))
for _, file := range changes {
fileName := file.ToPath
if file.ToPath != file.FromPath {
if len(file.Deleted) > 0 {
fileName = file.FromPath
} else {
fileName = file.ToPath
}
}
path := filepath.Join(repoRoot, fileName)
if strings.HasPrefix(path, cwd) { // take changes only inside project
files = append(
files, &ChangedFile{
Path: path,
Added: file.Added,
Deleted: file.Deleted,
},
)
}
}
sort.Slice(
files, func(i, j int) bool {
return files[i].Path < files[j].Path
},
)
return ChangedFiles{Files: files}, nil
}