func parseDiff()

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
}