difi/internal/git/client.go
2026-01-30 11:20:29 -05:00

121 lines
2.7 KiB
Go

package git
import (
"fmt"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
tea "github.com/charmbracelet/bubbletea"
)
func GetCurrentBranch() string {
out, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
if err != nil {
return "HEAD"
}
return strings.TrimSpace(string(out))
}
func GetRepoName() string {
out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
return "Repo"
}
path := strings.TrimSpace(string(out))
parts := strings.Split(path, "/")
if len(parts) > 0 {
return parts[len(parts)-1]
}
return "Repo"
}
func ListChangedFiles(targetBranch string) ([]string, error) {
cmd := exec.Command("git", "diff", "--name-only", targetBranch)
out, err := cmd.Output()
if err != nil {
return nil, err
}
files := strings.Split(strings.TrimSpace(string(out)), "\n")
if len(files) == 1 && files[0] == "" {
return []string{}, nil
}
return files, nil
}
func DiffCmd(targetBranch, path string) tea.Cmd {
return func() tea.Msg {
out, err := exec.Command("git", "diff", "--color=always", targetBranch, "--", path).Output()
if err != nil {
return DiffMsg{Content: "Error fetching diff: " + err.Error()}
}
return DiffMsg{Content: string(out)}
}
}
func OpenEditorCmd(path string, lineNumber int) tea.Cmd {
editor := os.Getenv("EDITOR")
if editor == "" {
if _, err := exec.LookPath("nvim"); err == nil {
editor = "nvim"
} else {
editor = "vim"
}
}
var args []string
if lineNumber > 0 {
args = append(args, fmt.Sprintf("+%d", lineNumber))
}
args = append(args, path)
c := exec.Command(editor, args...)
c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
return tea.ExecProcess(c, func(err error) tea.Msg {
return EditorFinishedMsg{Err: err}
})
}
func CalculateFileLine(diffContent string, visualLineIndex int) int {
lines := strings.Split(diffContent, "\n")
if visualLineIndex >= len(lines) {
return 0
}
re := regexp.MustCompile(`^.*?@@ \-\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@`)
currentLineNo := 0
for i := 0; i <= visualLineIndex; i++ {
line := lines[i]
matches := re.FindStringSubmatch(line)
if len(matches) > 1 {
startLine, _ := strconv.Atoi(matches[1])
currentLineNo = startLine
continue
}
cleanLine := stripAnsi(line)
if strings.HasPrefix(cleanLine, " ") || strings.HasPrefix(cleanLine, "+") {
currentLineNo++
}
}
if currentLineNo == 0 {
return 1
}
return currentLineNo - 1
}
func stripAnsi(str string) string {
const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
var re = regexp.MustCompile(ansi)
return re.ReplaceAllString(str, "")
}
type DiffMsg struct{ Content string }
type EditorFinishedMsg struct{ Err error }