135 lines
3.5 KiB
Go
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
|
|
}
|