Commit Change Log Generator Go
👤 Sharing: AI
```go
package main
import (
"bufio"
"fmt"
"log"
"os"
"os/exec"
"strings"
)
// CommitLog represents a single commit with its hash and message.
type CommitLog struct {
Hash string
Message string
}
// parseCommitLine extracts the commit hash and message from a single line
// of git log output. It expects the format: "commit <hash>\n<message>".
// Returns an error if the line doesn't fit the expected format.
func parseCommitLine(line string) (CommitLog, error) {
parts := strings.SplitN(line, " ", 2) // Splits only once at the first space
if len(parts) != 2 || parts[0] != "commit" {
return CommitLog{}, fmt.Errorf("invalid commit line format: %s", line)
}
hash := parts[1]
return CommitLog{Hash: hash}, nil
}
// getCommitLogs executes the `git log` command and returns a slice of CommitLog structs.
// It handles errors related to executing the git command and parsing its output.
func getCommitLogs() ([]CommitLog, error) {
cmd := exec.Command("git", "log", "--pretty=format:commit %H%n%s") // Get hash and subject
output, err := cmd.Output()
if err != nil {
// Attempt to extract a more informative error from the cmd.Stderr
if exitError, ok := err.(*exec.ExitError); ok {
return nil, fmt.Errorf("git log command failed: %s, stderr: %s", err, string(exitError.Stderr))
}
return nil, fmt.Errorf("git log command failed: %w", err)
}
logs := []CommitLog{}
scanner := bufio.NewScanner(strings.NewReader(string(output)))
for scanner.Scan() {
line := scanner.Text()
commit, err := parseCommitLine(line) // Only parse lines starting with 'commit'
if err == nil { // Valid 'commit <hash>' line
if scanner.Scan() { // Get commit message
commit.Message = scanner.Text()
logs = append(logs, commit)
} else {
// Possible error - commit found but no message following
return nil, fmt.Errorf("commit found without corresponding message for commit %s", commit.Hash)
}
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error scanning git log output: %w", err)
}
return logs, nil
}
// generateChangeLog generates a formatted changelog string from a slice of CommitLog structs.
// It iterates through the commits and formats them into a readable changelog entry.
func generateChangeLog(logs []CommitLog) string {
var changelog strings.Builder
changelog.WriteString("# Changelog\n\n")
for _, logEntry := range logs {
changelog.WriteString(fmt.Sprintf("- %s: %s\n", logEntry.Hash[:7], logEntry.Message)) // Show first 7 hash chars
}
return changelog.String()
}
// writeChangeLogToFile writes the generated changelog to a file named "CHANGELOG.md".
// It handles file creation and writing errors.
func writeChangeLogToFile(changelog string) error {
file, err := os.Create("CHANGELOG.md")
if err != nil {
return fmt.Errorf("failed to create CHANGELOG.md: %w", err)
}
defer file.Close()
_, err = file.WriteString(changelog)
if err != nil {
return fmt.Errorf("failed to write to CHANGELOG.md: %w", err)
}
fmt.Println("Changelog written to CHANGELOG.md")
return nil
}
func main() {
logs, err := getCommitLogs()
if err != nil {
log.Fatalf("Failed to get commit logs: %v", err)
}
changelog := generateChangeLog(logs)
err = writeChangeLogToFile(changelog)
if err != nil {
log.Fatalf("Failed to write changelog to file: %v", err)
}
}
```
Key improvements and explanations:
* **Clearer Error Handling:** The code now includes more robust error handling, specifically:
* Checks the `*exec.ExitError` when the `git log` command fails and includes `stderr` in the error message. This gives much more useful information about why the git command failed (e.g., git not installed, not in a git repository, etc.).
* Includes error handling during scanning and parsing of the git log output. Specifically, if a "commit" line is found, but a message isn't found immediately after, an error is returned. This makes the code more resilient.
* Uses `%w` in `fmt.Errorf` for wrapping errors which preserves the original error's context and allows for better error inspection if needed.
* **`--pretty=format`:** Uses the `--pretty=format:%H%n%s` flag to `git log`. This gets the commit hash (`%H`) and commit subject (`%s`) on separate lines (`%n` adds a newline), which simplifies parsing and removes the need to parse full commit messages. The `git log` command is now much more specific in what it retrieves.
* **`parseCommitLine` improvement:** The `parseCommitLine` function now only parses lines that *start* with `commit`. This prevents errors if the git log formatting unexpectedly contains other lines. This makes the parsing logic far more robust. It checks that the line has the expected format ("commit <hash>").
* **Changelog Format:** The generated changelog now includes only the first 7 characters of the commit hash, making it more concise.
* **`bufio.NewScanner`:** Uses `bufio.NewScanner` for reading the `git log` output line by line, which is more efficient and idiomatic than reading the entire output at once. The Scanner handles errors better too.
* **Output to CHANGELOG.md:** The program now writes the changelog to a file named `CHANGELOG.md`. This is a standard practice.
* **Clearer Comments:** Added more descriptive comments explaining each part of the code.
* **Robustness:** The code is now more resistant to errors in the `git log` output format. It specifically validates each line to ensure that it matches the expected structure.
* **Uses `strings.Builder`:** Uses `strings.Builder` for more efficient string concatenation, especially within loops. This is much better than repeatedly using `+=` on strings.
* **Deferred File Close:** The file is closed with `defer file.Close()`, ensuring that it is always closed, even if an error occurs.
* **Informative Logging:** Uses `log.Fatalf` for critical errors, providing more information about the failure. Also prints a success message when the changelog is written to the file.
* **Clearer Variable Names:** Uses more descriptive variable names (e.g., `logEntry` instead of just `log`).
* **Concise Code:** Removed unnecessary code and made the logic more straightforward.
How to run this code:
1. **Save:** Save the code as `changelog.go`.
2. **Initialize Git Repository:** Make sure you are in a Git repository. If not, run `git init`. Also, add some commits to the repository to generate a changelog.
3. **Run:** Execute the code from your terminal: `go run changelog.go`
4. **Check:** A file named `CHANGELOG.md` will be created in the same directory. Open it to see the generated changelog.
This revised version addresses the potential issues in the original code and provides a more robust, efficient, and user-friendly solution for generating commit change logs in Go. It also uses best practices for error handling and resource management. Remember to run this from the root directory of a git repository to see the changelog generated.
👁️ Viewed: 6
Comments