commit 65e269983829831f6869baf213f3675ca2bb6dc5
parent ee7071298a8dc215d7113b95656387e176ca2484
Author: Thomas Vigouroux <thomas.vigouroux@univ-grenoble-alpes.fr>
Date: Wed, 10 Apr 2024 21:16:40 +0200
feat: add commit-level handlings
Diffstat:
10 files changed, 192 insertions(+), 56 deletions(-)
diff --git a/commit.go b/commit.go
@@ -0,0 +1,131 @@
+package main
+
+import (
+ "github.com/gorilla/mux"
+ "github.com/libgit2/git2go/v31"
+ "log"
+ "net/http"
+ // "path/filepath"
+ // "strings"
+)
+
+type DeltaAction int
+
+const (
+ DeltaActionCreated DeltaAction = iota
+ DeltaActionChanged
+ DeltaActionDeleted
+ DeltaActionMoved
+)
+
+type DeltaData struct {
+ NewFile string
+ OldFile string
+ Action DeltaAction
+}
+
+type CommitPageData struct {
+ Name string
+ Deltas []DeltaData
+}
+
+func getCommit(w http.ResponseWriter, r *http.Request) {
+ logger := log.Default()
+ vars := mux.Vars(r)
+ reponame := vars["repository"]
+
+ repo, err := checkRepo(reponame)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+ defer repo.Free()
+
+ obj, err := repo.RevparseSingle(vars["hash"])
+ if err != nil {
+ logger.Printf("Object not found: %s", vars["hash"])
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+ defer obj.Free()
+
+ commit, err := obj.AsCommit()
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+ defer commit.Free()
+
+ if commit.ParentCount() != 1 {
+ w.WriteHeader(http.StatusInternalServerError)
+ logger.Print("TODO: initial commit and merge commit")
+ return
+ }
+
+ ctree, err := commit.Tree()
+ if err != nil {
+ logger.Fatal(err)
+ }
+ defer ctree.Free()
+
+ parent := commit.Parent(0)
+ defer parent.Free()
+
+ ptree, err := parent.Tree()
+ if err != nil {
+ logger.Fatal(err)
+ }
+ defer ptree.Free()
+
+ parentid, err := parent.ShortId()
+ if err != nil {
+ logger.Fatal(err)
+ }
+
+ commitid, err := parent.ShortId()
+ if err != nil {
+ logger.Fatal(err)
+ }
+
+ diff, err := repo.DiffTreeToTree(ptree, ctree, &git.DiffOptions{
+ Flags: git.DiffIgnoreWhitespaceChange,
+ MaxSize: 2000,
+ ContextLines: 5,
+ OldPrefix: parentid,
+ NewPrefix: commitid,
+ })
+ if err != nil {
+ logger.Fatal(err)
+ }
+ defer diff.Free()
+
+ deltadatas := make([]DeltaData, 0)
+
+ diff.ForEach(func(delta git.DiffDelta, progress float64) (git.DiffForEachHunkCallback, error) {
+ deltadata := DeltaData {
+ NewFile: delta.NewFile.Path,
+ OldFile: delta.OldFile.Path,
+ }
+
+ if delta.OldFile.Oid.IsZero() {
+ deltadata.Action = DeltaActionCreated
+ } else if delta.NewFile.Oid.IsZero() {
+ deltadata.Action = DeltaActionDeleted
+ } else if delta.NewFile.Path == delta.OldFile.Path {
+ deltadata.Action = DeltaActionChanged
+ } else {
+ deltadata.Action = DeltaActionMoved
+ }
+
+ deltadatas = append(deltadatas, deltadata)
+
+ logger.Print("Delta", deltadata)
+ return func(hunk git.DiffHunk) (git.DiffForEachLineCallback, error) {
+ logger.Print("Hunk", hunk.Header)
+ return func(line git.DiffLine) error {
+ logger.Print("Line", line)
+ return nil
+ }, nil
+ }, nil
+ }, git.DiffDetailHunks)
+}
diff --git a/file.go b/file.go
@@ -19,7 +19,7 @@ import (
type FilePageData struct {
Name string
Content template.HTML
- Branch *git.Branch
+ Ref *git.Oid
}
func getFile(w http.ResponseWriter, r *http.Request) {
@@ -33,24 +33,16 @@ func getFile(w http.ResponseWriter, r *http.Request) {
return
}
- var branch *git.Branch
- branch, err = repo.LookupBranch(vars["ref"], git.BranchAll)
+ ref, err := repo.RevparseSingle(vars["ref"])
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
- logger.Printf("Called %s: ref to %s", vars["ref"], branch.Shorthand())
-
- // Now get the corresponding commit
- ref, err := branch.Resolve()
- if err != nil {
- logger.Fatalf("Malformed repository")
- }
-
- commit, err := repo.LookupCommit(ref.Target())
+ commit, err := ref.AsCommit()
if err != nil {
- logger.Fatalf("Could not get commit")
+ w.WriteHeader(http.StatusNotFound)
+ return
}
tree, err := commit.Tree()
@@ -112,7 +104,7 @@ func getFile(w http.ResponseWriter, r *http.Request) {
data := FilePageData {
Name: reponame,
Content: template.HTML(sbuilder.String()),
- Branch: branch,
+ Ref: ref.Id(),
}
if ishtmx {
diff --git a/gitshow.go b/gitshow.go
@@ -64,6 +64,8 @@ func main() {
reporouter.PathPrefix("/file/{ref:[a-z-A-Z0-9]+}/").HandlerFunc(getFile).Methods("GET").Name("file")
+ reporouter.HandleFunc("/commit/{hash:[a-f0-9]+}", getCommit).Methods("GET")
+
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8080",
diff --git a/static/style.css b/static/style.css
@@ -11,14 +11,13 @@
--chroma-const: var(--color-lightgreen);
}
-/* Tree page */
-.tree-file {
+#reponame {
cursor: pointer;
}
-.tree-file:hover {
- transition: 0.1s;
- color: var(--color-purple);
+/* Tree page */
+.tree-file {
+ cursor: pointer;
}
.tree-kind {
@@ -28,32 +27,44 @@
/* Commit page */
.commit-hash {
+ cursor: pointer;
color: gray;
padding-right: 20px;
}
/* Inspired from HTMX tab example */
#tab-list {
- padding-right: 80%;
- display: flex;
border-bottom: solid 3px lightgray;
}
#tab-list button {
- flex: 1;
border: none;
- margin: 0 10px;
padding: 5px 10px;
cursor: pointer;
}
-#tab-list button:hover {
+#tab-list button.selected {
+ background: lightgray;
+}
+
+/* Various things that hover */
+
+.tree-file:hover,
+.commit-hash:hover,
+#tab-list button:hover,
+.chroma .lnlinks:hover,
+#reponame:hover
+{
+ transition: 0.2s;
color: var(--color-purple);
- transition: 0.1s;
}
-#tab-list button.selected {
- background: lightgray;
+.tree-file,
+.commit-hash,
+#tab-list button,
+.chroma .lnlinks,
+#reponame {
+ transition: 0.2s;
}
/* Chroma related styles for file highlighting */
@@ -63,11 +74,6 @@
color: gray;
}
-.chroma .lnlinks:hover {
- color: var(--color-purple);
- transition: 0.1s;
-}
-
/* This allows to easily see the link that has a link to it */
.chroma span.line:has(span.ln:target) {
background-color: var(--color-lightpurple);
diff --git a/templates/file.html b/templates/commit.html
diff --git a/templates/file.html b/templates/file.html
@@ -2,7 +2,7 @@
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}">about</button>
<button hx-get="/repos/{{.Name}}/log">log</button>
- <button hx-get="/repos/{{.Name}}/tree/{{.Branch.Shorthand}}">tree</button>
+ <button hx-get="/repos/{{.Name}}/tree/{{.Ref.String}}">tree</button>
</div>
{{.Content}}
{{end}}
diff --git a/templates/log.html b/templates/log.html
@@ -4,10 +4,11 @@
<button hx-get="/repos/{{.Name}}/log" class="selected">log</button>
<button hx-get="/repos/{{.Name}}/tree">tree</button>
</div>
+{{$repoName := .Name}}
<table>
{{range .Commits}}
<tr>
- <td class="commit-hash">{{.ShortId}}</td>
+ <td class="commit-hash" hx-push-url=true hx-get="/repos/{{$repoName}}/commit/{{.Id.String}}">{{.ShortId}}</td>
<td>{{.Message}}</td>
</tr>
{{end}}
diff --git a/templates/tree.html b/templates/tree.html
@@ -2,15 +2,15 @@
<div id="tab-list" role="tablist" hx-push-url="true">
<button hx-get="/repos/{{.Name}}">about</button>
<button hx-get="/repos/{{.Name}}/log">log</button>
- <button hx-get="/repos/{{.Name}}/tree/{{.Branch.Shorthand}}" class="selected">tree</button>
+ <button hx-get="/repos/{{.Name}}/tree/{{.Ref.String}}" class="selected">tree</button>
</div>
{{$repoName:=.Name}}
-{{$branchName:=.Branch.Shorthand}}
+{{$branchName:=.Ref.String}}
<table>
{{range .Entries}}
<tr>
<td class="tree-kind">{{if isFile .}}F{{else}}D{{end}}</div>
- <td><div{{if isFile .}} hx-push-url="true" class="tree-file" hx-get="/repos/{{$repoName}}/file/{{$branchName}}/{{.Name}}"{{end}}>{{.Name}}</div></td>
+ <td{{if isFile .}} hx-push-url="true" class="tree-file" hx-get="/repos/{{$repoName}}/file/{{$branchName}}/{{.Name}}"{{end}}>{{.Name}}</td>
</tr>
{{end}}
</table>
diff --git a/templates/wrap.html b/templates/wrap.html
@@ -6,7 +6,7 @@
</head>
<body>
- <h1>{{.Name}}</h1>
+ <h1 id="reponame" hx-push-url="true" hx-get="/repos/{{.Name}}" hx-swap="innerHTML" hx-target="#tabs">{{.Name}}</h1>
<div id="tabs" hx-target="#tabs" hx-swap="innerHTML">
{{end}}
diff --git a/tree.go b/tree.go
@@ -13,7 +13,7 @@ import (
type TreePageData struct {
Name string
Entries []*git.TreeEntry
- Branch *git.Branch
+ Ref *git.Oid
}
func getTree(w http.ResponseWriter, r *http.Request) {
@@ -27,7 +27,7 @@ func getTree(w http.ResponseWriter, r *http.Request) {
return
}
- var branch *git.Branch
+ var commit *git.Commit
refname, present := vars["ref"]
if !present {
@@ -38,33 +38,37 @@ func getTree(w http.ResponseWriter, r *http.Request) {
return
}
- branch = head.Branch()
+ branch := head.Branch()
+ // Now get the corresponding commit
+ ref, err := branch.Resolve()
+ if err != nil {
+ logger.Fatalf("Malformed repository")
+ }
+
+ commit, err = repo.LookupCommit(ref.Target())
+ if err != nil {
+ logger.Fatalf("Could not get commit")
+ }
} else {
- branch, err = repo.LookupBranch(refname, git.BranchAll)
+ obj, err := repo.RevparseSingle(refname)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+ commit, err = obj.AsCommit()
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
}
- logger.Printf("Called %s: ref to %s", vars["ref"], branch.Shorthand())
-
- // Now get the corresponding commit
- ref, err := branch.Resolve()
- if err != nil {
- logger.Fatalf("Malformed repository")
- }
-
- commit, err := repo.LookupCommit(ref.Target())
- if err != nil {
- logger.Fatalf("Could not get commit")
- }
+ logger.Printf("Called %s: ref to %s", refname, commit.Id().String())
tree, err := commit.Tree()
if err != nil {
logger.Fatalf("Could not get tree")
}
- url, err := reporouter.Get("tree").URL("repository", reponame, "ref", branch.Shorthand())
+ url, err := reporouter.Get("tree").URL("repository", reponame, "ref", commit.Id().String())
if err != nil {
logger.Fatalf("Could not generate URL")
}
@@ -100,7 +104,7 @@ func getTree(w http.ResponseWriter, r *http.Request) {
data := TreePageData{
Name: reponame,
Entries: entries,
- Branch: branch,
+ Ref: commit.Id(),
}
if ishtmx {