This commit introduces a new `changelog` command to the `borg` CLI. This command generates a human-readable changelog between two archive versions, addressing a key user request for tracking changes over time. Key features of the `changelog` command include: - **File Comparison:** Detects added, modified, and removed files between two archives. - **Diff Statistics:** Calculates line-level insertions and deletions for modified files. - **Multiple Output Formats:** Supports plain text, Markdown, and JSON output, controlled by a `--format` flag. - **Remote Source Comparison:** Allows comparing a local archive against a remote GitHub repository using the `--source` flag (e.g., `--source github:org/repo`). - **Commit Message Extraction:** When comparing archives that contain a `.git` repository, the command extracts and displays the relevant commit messages for each modified file, providing valuable context for the changes. To support this functionality, this commit also includes: - A new `pkg/changelog` package containing the core logic for comparing archives and generating change reports. - A bugfix in `pkg/datanode` to ensure `fs.WalkDir` functions correctly on the root of a `DataNode`, which was necessary for iterating through archive contents. - A modification to the `pkg/vcs` Git cloner to include the `.git` directory in the created `DataNode`, enabling commit history analysis. Co-authored-by: Snider <631881+Snider@users.noreply.github.com>
86 lines
2.4 KiB
Go
86 lines
2.4 KiB
Go
package changelog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// FormatAsText formats the ChangeReport as plain text.
|
|
func FormatAsText(report *ChangeReport) (string, error) {
|
|
var builder strings.Builder
|
|
|
|
if len(report.Added) > 0 {
|
|
builder.WriteString(fmt.Sprintf("Added (%d files):\n", len(report.Added)))
|
|
for _, file := range report.Added {
|
|
builder.WriteString(fmt.Sprintf("- %s\n", file))
|
|
}
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
if len(report.Modified) > 0 {
|
|
builder.WriteString(fmt.Sprintf("Modified (%d files):\n", len(report.Modified)))
|
|
for _, file := range report.Modified {
|
|
builder.WriteString(fmt.Sprintf("- %s (+%d -%d lines)\n", file.Path, file.Additions, file.Deletions))
|
|
for _, commit := range file.Commits {
|
|
builder.WriteString(fmt.Sprintf(" - %s\n", strings.Split(commit, "\n")[0]))
|
|
}
|
|
}
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
if len(report.Removed) > 0 {
|
|
builder.WriteString(fmt.Sprintf("Removed (%d files):\n", len(report.Removed)))
|
|
for _, file := range report.Removed {
|
|
builder.WriteString(fmt.Sprintf("- %s\n", file))
|
|
}
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
return builder.String(), nil
|
|
}
|
|
|
|
// FormatAsMarkdown formats the ChangeReport as Markdown.
|
|
func FormatAsMarkdown(report *ChangeReport) (string, error) {
|
|
var builder strings.Builder
|
|
|
|
builder.WriteString("# Changes\n\n")
|
|
|
|
if len(report.Added) > 0 {
|
|
builder.WriteString(fmt.Sprintf("## Added (%d files)\n", len(report.Added)))
|
|
for _, file := range report.Added {
|
|
builder.WriteString(fmt.Sprintf("- `%s`\n", file))
|
|
}
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
if len(report.Modified) > 0 {
|
|
builder.WriteString(fmt.Sprintf("## Modified (%d files)\n", len(report.Modified)))
|
|
for _, file := range report.Modified {
|
|
builder.WriteString(fmt.Sprintf("- `%s` (+%d -%d lines)\n", file.Path, file.Additions, file.Deletions))
|
|
for _, commit := range file.Commits {
|
|
builder.WriteString(fmt.Sprintf(" - *%s*\n", strings.Split(commit, "\n")[0]))
|
|
}
|
|
}
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
if len(report.Removed) > 0 {
|
|
builder.WriteString(fmt.Sprintf("## Removed (%d files)\n", len(report.Removed)))
|
|
for _, file := range report.Removed {
|
|
builder.WriteString(fmt.Sprintf("- `%s`\n", file))
|
|
}
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
return builder.String(), nil
|
|
}
|
|
|
|
// FormatAsJSON formats the ChangeReport as JSON.
|
|
func FormatAsJSON(report *ChangeReport) (string, error) {
|
|
data, err := json.MarshalIndent(report, "", " ")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|