Files
simplegit/gitcmd/writefile.go
T
2026-07-15 11:06:48 -04:00

135 lines
3.5 KiB
Go

package gitcmd
import (
"fmt"
"os"
"strings"
)
type FileCommitResult struct {
Commit Commit `json:"commit"`
}
func (r *Repository) CreateOrUpdateFile(branch, path, content, message, authorName, authorEmail, expectedSHA string) (*FileCommitResult, error) {
parent, err := r.refSHA("refs/heads/" + branch)
if err != nil {
return nil, err
}
if parent == "" && expectedSHA != "" {
return nil, fmt.Errorf("branch %q does not exist but expectedSHA %s given", branch, expectedSHA)
}
if parent != "" && expectedSHA != "" && parent != expectedSHA {
return nil, fmt.Errorf("branch %q moved: expected %s, current %s", branch, expectedSHA, parent)
}
blobSHA, err := r.hashObject(content, r.Path)
if err != nil {
return nil, err
}
newTree, err := r.withTempIndex(func(idx string) (string, error) {
return r.buildTree(idx, parent, path, blobSHA, false)
})
if err != nil {
return nil, err
}
var parents []string
if parent != "" {
parents = []string{parent}
}
commitSHA, err := r.commitTree(newTree, message, authorName, authorEmail, r.Path, parents...)
if err != nil {
return nil, err
}
if err := r.updateRef("refs/heads/"+branch, commitSHA, expectedSHA); err != nil {
return nil, err
}
commit, err := r.GetCommit(commitSHA)
if err != nil {
return nil, err
}
return &FileCommitResult{Commit: *commit}, nil
}
func (r *Repository) DeleteFile(branch, path, message, authorName, authorEmail, expectedSHA string) (*FileCommitResult, error) {
parent, err := r.refSHA("refs/heads/" + branch)
if err != nil {
return nil, err
}
if parent == "" {
return nil, fmt.Errorf("branch %q is empty; nothing to delete", branch)
}
if expectedSHA != "" && parent != expectedSHA {
return nil, fmt.Errorf("branch %q moved: expected %s, current %s", branch, expectedSHA, parent)
}
newTree, err := r.withTempIndex(func(idx string) (string, error) {
return r.buildTree(idx, parent, path, "", true)
})
if err != nil {
return nil, err
}
commitSHA, err := r.commitTree(newTree, message, authorName, authorEmail, r.Path, parent)
if err != nil {
return nil, err
}
if err := r.updateRef("refs/heads/"+branch, commitSHA, expectedSHA); err != nil {
return nil, err
}
commit, err := r.GetCommit(commitSHA)
if err != nil {
return nil, err
}
return &FileCommitResult{Commit: *commit}, nil
}
func (r *Repository) withTempIndex(fn func(idx string) (string, error)) (string, error) {
f, err := os.CreateTemp("", "simplegit-index-*")
if err != nil {
return "", err
}
name := f.Name()
f.Close()
_ = os.Remove(name)
defer os.Remove(name)
return fn(name)
}
func (r *Repository) buildTree(indexFile, parent, path, blobSHA string, remove bool) (string, error) {
env := "GIT_INDEX_FILE=" + indexFile
if parent != "" {
if _, _, err := NewCommand("read-tree").
AddDynamicArguments(parent).
WithDir(r.Path).WithEnv(env).
RunStdString(r.ctx); err != nil {
return "", fmt.Errorf("read-tree: %w", err)
}
}
if remove {
if err := r.removePathFromIndex(r.Path, path, env); err != nil {
return "", err
}
} else {
if _, _, err := NewCommand("update-index", "--add", "--replace", "--cacheinfo").
AddDynamicArguments("100644," + blobSHA + "," + path).
WithDir(r.Path).WithEnv(env).
RunStdString(r.ctx); err != nil {
return "", fmt.Errorf("update-index --cacheinfo: %w", err)
}
}
out, _, err := NewCommand("write-tree").
WithDir(r.Path).WithEnv(env).
RunStdString(r.ctx)
if err != nil {
return "", fmt.Errorf("write-tree: %w", err)
}
return strings.TrimSpace(out), nil
}