git2go
git2go copied to clipboard
How to pass the equivalent of git push -o <string>?
On the Git CLI, I can use git push -o <option> for example git push -o merge_request.create to supply string options with push that are then processed on the server by i.e. the post-receive hook to perform additional actions, like create a pull request automatically after the push etc. See GitLab's options as an example.
My failed attempt to recreate this with git2go :
package main
import (
git "github.com/libgit2/git2go/v34"
log "github.com/sirupsen/logrus"
)
func main() {
repo, err := git.OpenRepository("/home/ml/Projects/demo")
if err != nil {
log.Fatal(err)
}
remote, err := repo.Remotes.Lookup("origin")
if err != nil {
remote, err = repo.Remotes.Create("origin", repo.Path())
if err != nil {
log.Fatal(err)
}
}
// ref
ref, err := repo.Head()
if err != nil {
log.Fatal(err)
}
// Get the name
branch := ref.Branch()
branchName, err := branch.Name()
if err != nil {
log.Fatal(err)
}
if err := remote.Push([]string{"refs/heads/" + branchName}, &git.PushOptions{
RemoteCallbacks: git.RemoteCallbacks{
CredentialsCallback: func(url string, username_from_url string, allowed_types git.CredentialType) (*git.Credential, error) {
return git.NewCredentialUserpassPlaintext("username", "pass")
},
},
Headers: []string{"merge_request.create:true"},
}); err != nil {
log.Fatal(err)
}
}
Is there a way to do this?
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
git "github.com/libgit2/git2go/v34"
log "github.com/sirupsen/logrus"
)
// createCredentialHelper creates a temporary credential helper script for git push.
func createCredentialHelper(username, password string) (string, error) {
// Create a temporary file for the credential helper script
tmpDir := os.TempDir()
scriptPath := filepath.Join(tmpDir, "git-credential-helper.sh")
// Write a simple shell script to provide username and password
scriptContent := fmt.Sprintf(`#!/bin/sh
echo username=%s
echo password=%s
`, username, password)
if err := os.WriteFile(scriptPath, []byte(scriptContent), 0700); err != nil {
return "", fmt.Errorf("failed to write credential helper: %w", err)
}
return scriptPath, nil
}
// pushWithOptions performs a git push with the specified push options using the Git CLI.
func pushWithOptions(repoPath, branchName, remoteName string, pushOptions []string, username, password string) error {
// Create credential helper
credHelperPath, err := createCredentialHelper(username, password)
if err != nil {
return err
}
defer os.Remove(credHelperPath)
// Build the git push command
args := []string{
"push",
remoteName,
"refs/heads/" + branchName,
}
for _, opt := range pushOptions {
args = append(args, "-o", opt)
}
cmd := exec.Command("git", args...)
cmd.Dir = repoPath
cmd.Env = append(os.Environ(),
fmt.Sprintf("GIT_ASKPASS=%s", credHelperPath),
"GIT_TERMINAL_PROMPT=0", // Disable interactive prompts
)
// Capture output for logging
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("git push failed: %s\nOutput: %s", err, string(output))
}
log.Infof("git push succeeded: %s", string(output))
return nil
}
func main() {
// Initialize logging
log.SetFormatter(&log.TextFormatter{})
log.SetLevel(log.InfoLevel)
// Open the repository
repoPath := "/home/ml/Projects/demo"
repo, err := git.OpenRepository(repoPath)
if err != nil {
log.Fatalf("Failed to open repository: %v", err)
}
defer repo.Free()
// Lookup or create the remote
remoteName := "origin"
remote, err := repo.Remotes.Lookup(remoteName)
if err != nil {
remote, err = repo.Remotes.Create(remoteName, repo.Path())
if err != nil {
log.Fatalf("Failed to create remote: %v", err)
}
}
defer remote.Free()
// Get the current branch
ref, err := repo.Head()
if err != nil {
log.Fatalf("Failed to get HEAD: %v", err)
}
defer ref.Free()
branch := ref.Branch()
branchName, err := branch.Name()
if err != nil {
log.Fatalf("Failed to get branch name: %v", err)
}
// Perform git push with push options
pushOptions := []string{
"merge_request.create",
// Add more options as needed, e.g., "merge_request.target=main"
}
username := "username" // Replace with actual username
password := "pass" // Replace with actual password or token
if err := pushWithOptions(repoPath, branchName, remoteName, pushOptions, username, password); err != nil {
log.Fatalf("Push failed: %v", err)
}
log.Info("Push completed successfully")
}