Skip to content

Instantly share code, notes, and snippets.

@vicendominguez
Created March 7, 2025 14:03
Show Gist options
  • Save vicendominguez/3eb49986627a378388dfa7e62acf38df to your computer and use it in GitHub Desktop.
Save vicendominguez/3eb49986627a378388dfa7e62acf38df to your computer and use it in GitHub Desktop.
AI git commit message and code
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/pterm/pterm"
)
func main() {
// Open the current repository
repo, err := git.PlainOpen(".")
if err != nil {
pterm.Error.Printf("Error opening the repository: %v\n", err)
return
}
// Get the current branch name
branchName, err := getCurrentBranch(repo)
if err != nil {
pterm.Error.Printf("Error: %v\n", err)
pterm.Info.Println("Please create a branch first using 'git branch <branch-name>'.")
return
}
// Get the diff of changes in the staging area
diff, err := getGitDiffCached(repo)
if err != nil {
pterm.Error.Printf("Error getting the cached diff: %v\n", err)
return
}
// Generate the commit message using Ollama
commitMessage, err := generateCommitMessage(diff)
if err != nil {
pterm.Error.Printf("Error generating the commit message: %v\n", err)
return
}
// Add the branch name to the commit message
fullCommitMessage := fmt.Sprintf("[%s] %s", branchName, commitMessage)
// Create the commit
err = createGitCommit(repo, fullCommitMessage)
if err != nil {
pterm.Error.Printf("Error creating the commit: %v\n", err)
return
}
pterm.Success.Printf("Commit created successfully: %s\n", fullCommitMessage)
}
// Get the current branch name
func getCurrentBranch(repo *git.Repository) (string, error) {
ref, err := repo.Head()
if err != nil {
// Si el repositorio está vacío (no hay commits), devuelve un error
if err == plumbing.ErrReferenceNotFound {
return "", fmt.Errorf("no commits found (repository is empty)")
}
return "", err
}
// Verifica si HEAD apunta a una rama
if !ref.Name().IsBranch() {
return "", fmt.Errorf("HEAD is not pointing to a branch")
}
return ref.Name().Short(), nil
}
// Get the diff of changes in the staging area
func getGitDiffCached(repo *git.Repository) (string, error) {
worktree, err := repo.Worktree()
if err != nil {
return "", err
}
// Get the working tree status
status, err := worktree.Status()
if err != nil {
return "", err
}
// Simulate a diff (go-git does not have a direct method for `git diff --cached`)
var diffBuilder strings.Builder
for file, status := range status {
if status.Staging != git.Unmodified {
diffBuilder.WriteString(fmt.Sprintf("File: %s, Changes: %b\n", file, status.Staging))
}
}
return diffBuilder.String(), nil
}
// Generate the commit message using Ollama
func generateCommitMessage(diff string) (string, error) {
// URL of the Ollama API (replace this with the correct URL)
apiURL := "http://localhost:11434/api/generate"
model := "llama3.1"
// Create the request body
requestBody, err := json.Marshal(map[string]interface{}{
"model": model,
"prompt": "Analyze the following code diff and generate a summarized and concise commit message that describes the changes made. Include only the commit message itself, without any introduction or conclusion. Diff:\n" + diff,
"stream": false,
})
if err != nil {
return "", fmt.Errorf("error creating the request body: %v", err)
}
// Send the HTTP POST request to Ollama
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(requestBody))
if err != nil {
return "", fmt.Errorf("error sending the request to Ollama: %v", err)
}
defer resp.Body.Close()
// Check the response status code
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("unsuccessful response from Ollama: %s", resp.Status)
}
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return "", fmt.Errorf("error decoding the response from Ollama: %v", err)
}
// Get the generated commit message
commitMessage, ok := result["response"].(string)
if !ok {
return "", fmt.Errorf("the response from Ollama does not contain a commit message")
}
return commitMessage, nil
}
// Create the commit
func createGitCommit(repo *git.Repository, message string) error {
worktree, err := repo.Worktree()
if err != nil {
return err
}
// Add all changes to the staging area
_, err = worktree.Add(".")
if err != nil {
return err
}
// Obtener la configuración global y local
globalCfg, err := repo.ConfigScoped(config.GlobalScope)
if err != nil {
return fmt.Errorf("error reading global Git configuration: %v", err)
}
localCfg, err := repo.ConfigScoped(config.LocalScope)
if err != nil {
return fmt.Errorf("error reading local Git configuration: %v", err)
}
// Combinar las configuraciones: priorizar la configuración local sobre la global
userName := localCfg.User.Name
if userName == "" {
userName = globalCfg.User.Name
}
userEmail := localCfg.User.Email
if userEmail == "" {
userEmail = globalCfg.User.Email
}
if userName == "" || userEmail == "" {
return fmt.Errorf("user name or email not found in Git configuration")
}
now := time.Now()
// Create the commit
commit, err := worktree.Commit(message, &git.CommitOptions{
Author: &object.Signature{
Name: userName,
Email: userEmail,
When: now,
},
})
if err != nil {
return err
}
// Print the hash of the created commit
pterm.Info.Printf("Commit created with hash: %s\n", commit.String())
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment