Compare commits
2 commits
2282cdecfd
...
5f71f435f7
Author | SHA1 | Date | |
---|---|---|---|
|
5f71f435f7 | ||
|
6a1b122d2d |
5 changed files with 89 additions and 24 deletions
|
@ -21,7 +21,7 @@ Project heavily inspired by the amazing
|
||||||
- [ ] Add a subcommand to dump the embedded templates so they can be
|
- [ ] Add a subcommand to dump the embedded templates so they can be
|
||||||
modified.
|
modified.
|
||||||
- [ ] Take binary files into account.
|
- [ ] Take binary files into account.
|
||||||
- [ ] Limit the output for large diffs. Add a "X commits remaining"
|
- [X] Limit the output for large diffs. Add a "X commits remaining"
|
||||||
message if necessary.
|
message if necessary.
|
||||||
- [ ] Generate files in temporal directory and replace the final one
|
- [ ] Generate files in temporal directory and replace the final one
|
||||||
when everything is ready.
|
when everything is ready.
|
||||||
|
|
2
cmd.go
2
cmd.go
|
@ -14,7 +14,7 @@ type repoCmd struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repoCmd) Run() error {
|
func (r *repoCmd) Run() error {
|
||||||
return generateRepo(r.Path)
|
return generateRepo(r.Path, r.LogLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
var cli struct {
|
var cli struct {
|
||||||
|
|
64
gitssg.go
64
gitssg.go
|
@ -3,8 +3,10 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -42,6 +44,14 @@ type CommitFile struct {
|
||||||
Size int64
|
Size int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileStats struct {
|
||||||
|
Name string
|
||||||
|
Mode string // ToDo: is the name correct here?
|
||||||
|
Adds int
|
||||||
|
Dels int
|
||||||
|
Total int
|
||||||
|
}
|
||||||
|
|
||||||
type CommitInfo struct {
|
type CommitInfo struct {
|
||||||
Hash string
|
Hash string
|
||||||
ParentHash string
|
ParentHash string
|
||||||
|
@ -52,6 +62,7 @@ type CommitInfo struct {
|
||||||
Files int
|
Files int
|
||||||
Adds int
|
Adds int
|
||||||
Dels int
|
Dels int
|
||||||
|
FileStats []*FileStats
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed templates
|
//go:embed templates
|
||||||
|
@ -90,6 +101,9 @@ var funcMap = template.FuncMap{
|
||||||
"timeLongFormat": func(t time.Time) string {
|
"timeLongFormat": func(t time.Time) string {
|
||||||
return t.Format(timeLongFormatStr)
|
return t.Format(timeLongFormatStr)
|
||||||
},
|
},
|
||||||
|
"repeatStr": func(s string, n int) string {
|
||||||
|
return strings.Repeat(s, n)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeTemplate(name string, data any) ([]byte, error) {
|
func executeTemplate(name string, data any) ([]byte, error) {
|
||||||
|
@ -205,8 +219,8 @@ func generateIndex(args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateRepo(path string) error {
|
func generateRepo(path string, logLimit int) error {
|
||||||
slog.Debug("Generating repository", "path", path)
|
slog.Debug("Generating repository", "path", path, "logLimit", logLimit)
|
||||||
|
|
||||||
repoInfo := &RepoInfo{Name: strings.TrimSuffix(filepath.Base(path), ".git")}
|
repoInfo := &RepoInfo{Name: strings.TrimSuffix(filepath.Base(path), ".git")}
|
||||||
|
|
||||||
|
@ -321,7 +335,6 @@ func generateRepo(path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate the files index file
|
// generate the files index file
|
||||||
// ToDo: bundle execute and write into a function
|
|
||||||
filesData := map[string]any{"repoInfo": repoInfo, "files": files, "relpath": ""}
|
filesData := map[string]any{"repoInfo": repoInfo, "files": files, "relpath": ""}
|
||||||
filesDstpath := filepath.Join(repoInfo.Name, "files.html")
|
filesDstpath := filepath.Join(repoInfo.Name, "files.html")
|
||||||
if err := executeTemplateToFile("files", filesData, filesDstpath); err != nil {
|
if err := executeTemplateToFile("files", filesData, filesDstpath); err != nil {
|
||||||
|
@ -334,23 +347,52 @@ func generateRepo(path string) error {
|
||||||
return fmt.Errorf("cannot get git log for repository: %w", err)
|
return fmt.Errorf("cannot get git log for repository: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
remaining := 0
|
||||||
commits := []*CommitInfo{}
|
commits := []*CommitInfo{}
|
||||||
ciErr := cIter.ForEach(func(c *object.Commit) error {
|
for {
|
||||||
|
i++
|
||||||
|
c, err := cIter.Next()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot get next commit while processing log: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
slog.Debug("Processing commit", "hash", c.Hash)
|
slog.Debug("Processing commit", "hash", c.Hash)
|
||||||
|
|
||||||
|
if i > logLimit {
|
||||||
|
remaining++
|
||||||
|
slog.Debug("Limit reached while processing log", "iteration", i, "remaining", remaining)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// ToDo: is this the best way to get the number of files?
|
// ToDo: is this the best way to get the number of files?
|
||||||
fstats, err := c.Stats()
|
fstats, err := c.Stats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot get commit stats %s: %w", c.Hash, err)
|
return fmt.Errorf("cannot get commit stats %s: %w", c.Hash, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileStats := make([]*FileStats, len(fstats))
|
||||||
|
for i, fs := range fstats {
|
||||||
|
fileStats[i] = &FileStats{
|
||||||
|
Name: fs.Name,
|
||||||
|
Mode: "",
|
||||||
|
Adds: fs.Addition,
|
||||||
|
Dels: fs.Deletion,
|
||||||
|
Total: fs.Addition + fs.Deletion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commit := &CommitInfo{
|
commit := &CommitInfo{
|
||||||
Hash: c.Hash.String(),
|
Hash: c.Hash.String(),
|
||||||
Date: c.Author.When, // ToDo: author when vs commiter when?
|
Date: c.Author.When, // ToDo: author when vs commiter when?
|
||||||
Msg: c.Message,
|
Msg: c.Message,
|
||||||
AuthorName: c.Author.Name,
|
AuthorName: c.Author.Name,
|
||||||
AuthorEmail: c.Author.Email,
|
AuthorEmail: c.Author.Email,
|
||||||
Files: len(fstats),
|
Files: len(fstats), // ToDo: call len on FileStats
|
||||||
|
FileStats: fileStats,
|
||||||
}
|
}
|
||||||
|
|
||||||
if parent, _ := c.Parent(0); parent != nil {
|
if parent, _ := c.Parent(0); parent != nil {
|
||||||
|
@ -371,14 +413,14 @@ func generateRepo(path string) error {
|
||||||
if err := executeTemplateToFile("commit", data, dstpath); err != nil {
|
if err := executeTemplateToFile("commit", data, dstpath); err != nil {
|
||||||
return fmt.Errorf("cannot execute template %q to file %q: %w", "commit", dstpath, err)
|
return fmt.Errorf("cannot execute template %q to file %q: %w", "commit", dstpath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if ciErr != nil {
|
|
||||||
return fmt.Errorf("error while processing log: %w", ciErr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logData := map[string]any{"repoInfo": repoInfo, "commits": commits, "relpath": ""}
|
logData := map[string]any{
|
||||||
|
"repoInfo": repoInfo,
|
||||||
|
"commits": commits,
|
||||||
|
"relpath": "",
|
||||||
|
"remaining": remaining,
|
||||||
|
}
|
||||||
logDstpath := filepath.Join(repoInfo.Name, "log.html")
|
logDstpath := filepath.Join(repoInfo.Name, "log.html")
|
||||||
if err := executeTemplateToFile("log", logData, logDstpath); err != nil {
|
if err := executeTemplateToFile("log", logData, logDstpath); err != nil {
|
||||||
return fmt.Errorf("cannot execute template %q to file %q: %w", "file", logDstpath, err)
|
return fmt.Errorf("cannot execute template %q to file %q: %w", "file", logDstpath, err)
|
||||||
|
|
|
@ -40,17 +40,37 @@
|
||||||
|
|
||||||
{{.commit.Msg}}
|
{{.commit.Msg}}
|
||||||
<b>Diffstat:</b>
|
<b>Diffstat:</b>
|
||||||
<table><tr><td class="M">M</td><td><a href="#h0">LICENSE</a></td><td> | </td><td class="num">2</td><td><span class="i">+</span><span class="d">-</span></td></tr></table></pre>
|
<table>
|
||||||
<pre>1 file changed, 1 insertion(+), 1 deletion(-)
|
{{range $i, $fs := .commit.FileStats}}
|
||||||
<hr/><b>diff --git a/<a id="h0" href="../file/LICENSE.html">LICENSE</a> b/<a href="../file/LICENSE.html">LICENSE</a></b>
|
<tr>
|
||||||
<a href="#h0-0" id="h0-0" class="h">@@ -1,6 +1,6 @@
|
<td class="{{$fs.Mode}}">{{$fs.Mode}}</td>
|
||||||
</a> MIT/X Consortium License
|
<td><a href="#h{{$i}}">{{$fs.Name}}</a></td>
|
||||||
|
<td> | </td>
|
||||||
|
<td class="num">{{$fs.Total}}</td>
|
||||||
|
<td>{{if gt $fs.Adds 0}}<span class="i">{{repeatStr "+" $fs.Adds}}</span>{{end}}{{if gt $fs.Dels 0}}<span class="d">{{repeatStr "-" $fs.Dels}}</span>{{end}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table></pre><pre>{{.commit.Files}} file{{if ne .commit.Files 1}}s{{end}} changed, {{.commit.Adds}} insertion{{if ne .commit.Adds 1}}s{{end}}(+), {{.commit.Dels}} deletion{{if ne .commit.Dels 1}}s{{end}}(-)
|
||||||
|
<hr/><b>diff --git a/<a id="h0" href="../file/stagit-index.c.html">stagit-index.c</a> b/<a href="../file/stagit-index.c.html">stagit-index.c</a></b>
|
||||||
|
<a href="#h0-0" id="h0-0" class="h">@@ -179,7 +179,7 @@ main(int argc, char *argv[])
|
||||||
|
</a> int i, ret = 0;
|
||||||
|
|
||||||
<a href="#h0-0-2" id="h0-0-2" class="d">-(c) 2015-2022 Hiltjo Posthuma <hiltjo@codemadness.org>
|
if (argc < 2) {
|
||||||
</a><a href="#h0-0-3" id="h0-0-3" class="i">+(c) 2015-2024 Hiltjo Posthuma <hiltjo@codemadness.org>
|
<a href="#h0-0-3" id="h0-0-3" class="d">- fprintf(stderr, "%s [repodir...]\n", argv[0]);
|
||||||
</a>
|
</a><a href="#h0-0-4" id="h0-0-4" class="i">+ fprintf(stderr, "usage: %s [repodir...]\n", argv[0]);
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
</a> return 1;
|
||||||
copy of this software and associated documentation files (the "Software"),
|
}
|
||||||
|
|
||||||
|
<b>diff --git a/<a id="h1" href="../file/stagit.c.html">stagit.c</a> b/<a href="../file/stagit.c.html">stagit.c</a></b>
|
||||||
|
<a href="#h1-0" id="h1-0" class="h">@@ -1184,7 +1184,7 @@ writerefs(FILE *fp)
|
||||||
|
</a> void
|
||||||
|
usage(char *argv0)
|
||||||
|
{
|
||||||
|
<a href="#h1-0-3" id="h1-0-3" class="d">- fprintf(stderr, "%s [-c cachefile | -l commits] "
|
||||||
|
</a><a href="#h1-0-4" id="h1-0-4" class="i">+ fprintf(stderr, "usage: %s [-c cachefile | -l commits] "
|
||||||
|
</a> "[-u baseurl] repodir\n", argv0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -54,6 +54,9 @@
|
||||||
<td class="num" align="right">-{{.Dels}}</td>
|
<td class="num" align="right">-{{.Dels}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
{{if ne .remaining 0}}
|
||||||
|
<tr><td></td><td colspan="5">{{.remaining}} more commit{{if ne .remaining 1}}s{{end}} remaining, fetch the repository</td></tr>
|
||||||
|
{{end}}
|
||||||
</tbody></table></div>
|
</tbody></table></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue