143 lines
4.1 KiB
Go
143 lines
4.1 KiB
Go
package gitcmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func (r *Repository) tempClone() (temp string, cleanup func(), err error) {
|
|
dir, err := os.MkdirTemp("", "simplegit-temprepo-*")
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
temp = filepath.Join(dir, "repo.git")
|
|
cleanup = func() { _ = os.RemoveAll(dir) }
|
|
if _, _, err := NewCommand("clone", "--bare", "--shared").
|
|
AddDynamicArguments(r.Path, temp).
|
|
RunStdString(r.ctx); err != nil {
|
|
cleanup()
|
|
return "", nil, fmt.Errorf("temp clone: %w", err)
|
|
}
|
|
return temp, cleanup, nil
|
|
}
|
|
|
|
func (r *Repository) pushBack(temp, branch, commitSHA, expectedSHA string) error {
|
|
if expectedSHA != "" {
|
|
cur, err := r.refSHA("refs/heads/" + branch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if cur != expectedSHA {
|
|
return fmt.Errorf("branch %q moved: expected %s, current %s", branch, expectedSHA, cur)
|
|
}
|
|
}
|
|
if _, _, err := NewCommand("push", "origin").
|
|
AddDynamicArguments(commitSHA + ":refs/heads/" + branch).
|
|
WithDir(temp).RunStdString(r.ctx); err != nil {
|
|
return fmt.Errorf("push: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Repository) CreateOrUpdateFileViaTempRepo(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)
|
|
}
|
|
|
|
temp, cleanup, err := r.tempClone()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cleanup()
|
|
|
|
if parent != "" {
|
|
if _, _, err := NewCommand("read-tree").AddDynamicArguments(parent).WithDir(temp).RunStdString(r.ctx); err != nil {
|
|
return nil, fmt.Errorf("read-tree: %w", err)
|
|
}
|
|
}
|
|
blobSHA, err := r.hashObject(content, temp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, _, err := NewCommand("update-index", "--add", "--replace", "--cacheinfo").
|
|
AddDynamicArguments("100644," + blobSHA + "," + path).WithDir(temp).RunStdString(r.ctx); err != nil {
|
|
return nil, fmt.Errorf("update-index: %w", err)
|
|
}
|
|
treeOut, _, err := NewCommand("write-tree").WithDir(temp).RunStdString(r.ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("write-tree: %w", err)
|
|
}
|
|
newTree := strings.TrimSpace(treeOut)
|
|
|
|
var parents []string
|
|
if parent != "" {
|
|
parents = []string{parent}
|
|
}
|
|
commitSHA, err := r.commitTree(newTree, message, authorName, authorEmail, temp, parents...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := r.pushBack(temp, 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) DeleteFileViaTempRepo(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)
|
|
}
|
|
|
|
temp, cleanup, err := r.tempClone()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cleanup()
|
|
|
|
if _, _, err := NewCommand("read-tree").AddDynamicArguments(parent).WithDir(temp).RunStdString(r.ctx); err != nil {
|
|
return nil, fmt.Errorf("read-tree: %w", err)
|
|
}
|
|
|
|
if err := r.removePathFromIndex(temp, path); err != nil {
|
|
return nil, err
|
|
}
|
|
treeOut, _, err := NewCommand("write-tree").WithDir(temp).RunStdString(r.ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("write-tree: %w", err)
|
|
}
|
|
newTree := strings.TrimSpace(treeOut)
|
|
|
|
commitSHA, err := r.commitTree(newTree, message, authorName, authorEmail, temp, parent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := r.pushBack(temp, branch, commitSHA, expectedSHA); err != nil {
|
|
return nil, err
|
|
}
|
|
commit, err := r.GetCommit(commitSHA)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &FileCommitResult{Commit: *commit}, nil
|
|
}
|