Implement AsciiDoc and TipTap conversion logic, including new Admonition node and CodeBlock extension; add DiffViewer and HistoryPanel components for document version comparison; introduce password hashing utility with SHA-256.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -49,28 +50,43 @@ func (r *Repo) Commit(path, content, message, author, email string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Log returns the commit history for a file.
|
||||
// Log returns the commit history for a file, including added/removed line counts.
|
||||
func (r *Repo) Log(path string) ([]LogEntry, error) {
|
||||
out, err := r.run("log", "--format=%H|%an|%ae|%ai|%s", "--", path)
|
||||
out, err := r.run("log", "--format=COMMIT|%H|%an|%ae|%ai|%s", "--numstat", "--", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entries []LogEntry
|
||||
var current *LogEntry
|
||||
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "|", 5)
|
||||
if len(parts) == 5 {
|
||||
entries = append(entries, LogEntry{
|
||||
Hash: parts[0],
|
||||
Author: parts[1],
|
||||
Email: parts[2],
|
||||
Date: parts[3],
|
||||
Subject: parts[4],
|
||||
})
|
||||
if strings.HasPrefix(line, "COMMIT|") {
|
||||
if current != nil {
|
||||
entries = append(entries, *current)
|
||||
}
|
||||
parts := strings.SplitN(strings.TrimPrefix(line, "COMMIT|"), "|", 5)
|
||||
if len(parts) == 5 {
|
||||
current = &LogEntry{
|
||||
Hash: parts[0],
|
||||
Author: parts[1],
|
||||
Email: parts[2],
|
||||
Date: parts[3],
|
||||
Subject: parts[4],
|
||||
}
|
||||
}
|
||||
} else if current != nil && strings.Contains(line, "\t") {
|
||||
// numstat line: "added\tremoved\tfilename"
|
||||
fields := strings.SplitN(line, "\t", 3)
|
||||
if len(fields) == 3 {
|
||||
added, _ := strconv.Atoi(fields[0])
|
||||
removed, _ := strconv.Atoi(fields[1])
|
||||
current.Added += added
|
||||
current.Removed += removed
|
||||
}
|
||||
}
|
||||
}
|
||||
if current != nil {
|
||||
entries = append(entries, *current)
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
@@ -85,12 +101,34 @@ func (r *Repo) Show(hash, path string) (string, error) {
|
||||
return r.run("show", fmt.Sprintf("%s:%s", hash, path))
|
||||
}
|
||||
|
||||
// HasUncommitted returns true if the working tree contains untracked or
|
||||
// modified files that have not yet been committed.
|
||||
func (r *Repo) HasUncommitted() bool {
|
||||
out, err := r.run("status", "--porcelain")
|
||||
return err == nil && strings.TrimSpace(out) != ""
|
||||
}
|
||||
|
||||
// CommitAll stages every file in the repo root and creates a commit.
|
||||
func (r *Repo) CommitAll(message, author, email string) error {
|
||||
if _, err := r.run("add", "-A"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := r.run(
|
||||
"-c", fmt.Sprintf("user.name=%s", author),
|
||||
"-c", fmt.Sprintf("user.email=%s", email),
|
||||
"commit", "-m", message,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
type LogEntry struct {
|
||||
Hash string
|
||||
Author string
|
||||
Email string
|
||||
Date string
|
||||
Subject string
|
||||
Added int
|
||||
Removed int
|
||||
}
|
||||
|
||||
func writeFile(path, content string) error {
|
||||
|
||||
Reference in New Issue
Block a user