diff options
-rw-r--r-- | handlers/handler.go | 1 | ||||
-rw-r--r-- | handlers/repo-log.go | 15 | ||||
-rw-r--r-- | handlers/repo-plain.go | 8 | ||||
-rw-r--r-- | handlers/repo-refs.go | 15 | ||||
-rw-r--r-- | handlers/repo-root.go | 25 | ||||
-rw-r--r-- | handlers/repo-tree.go | 18 | ||||
-rw-r--r-- | models/repo.go | 29 | ||||
-rw-r--r-- | routers/main.go | 25 | ||||
-rw-r--r-- | routers/repo.go | 61 | ||||
-rw-r--r-- | static/style.css | 17 | ||||
-rw-r--r-- | templates/ctx/repo-log.go | 4 | ||||
-rw-r--r-- | templates/ctx/repo-refs.go | 4 | ||||
-rw-r--r-- | templates/ctx/repo-root.go | 4 | ||||
-rw-r--r-- | templates/ctx/repo-tree.go | 4 | ||||
-rw-r--r-- | templates/ctx/repo.go | 14 | ||||
-rw-r--r-- | templates/list.html | 2 | ||||
-rw-r--r-- | templates/repo-tree.html | 8 | ||||
-rw-r--r-- | templates/repo.html | 14 | ||||
-rw-r--r-- | templates/url/reverser.go | 10 |
19 files changed, 222 insertions, 56 deletions
diff --git a/handlers/handler.go b/handlers/handler.go index a331265..11273d1 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -10,6 +10,7 @@ type Request interface { // If the registered path for this handler is /X/Y/, return /Z/ for // Path() == /X/Y/Z/ Subtree() string + QueryString() map[string]string // Actions Redirect(string) diff --git a/handlers/repo-log.go b/handlers/repo-log.go index d34be97..7867ecb 100644 --- a/handlers/repo-log.go +++ b/handlers/repo-log.go @@ -19,7 +19,20 @@ func NewRepoLog(cfg *config.Global, url url.Reverser, repo models.Repo, consumer } func (rl *RepoLog) Serve(r Request) { - err := rl.consumer.Consume(r, ctx.NewRepoLog(rl.cfg, rl.url, rl.repo)) + branch := r.QueryString()["h"] + + if branch == "" { + branch = rl.repo.DefaultBranch() + } + + branches, err := rl.repo.Branches() + + if err != nil { + r.Error(err) + return + } + + err = rl.consumer.Consume(r, ctx.NewRepoLog(rl.cfg, rl.url, rl.repo, branch, branches)) if err != nil { r.Error(err) diff --git a/handlers/repo-plain.go b/handlers/repo-plain.go index 8f14cf3..64ea4a0 100644 --- a/handlers/repo-plain.go +++ b/handlers/repo-plain.go @@ -20,8 +20,14 @@ func (rp *RepoPlain) Serve(r Request) { return } + branch := r.QueryString()["h"] + + if branch == "" { + branch = rp.repo.DefaultBranch() + } + path := r.Subtree() - file, err := rp.repo.Find(rp.repo.DefaultBranch(), path) + file, err := rp.repo.Find(branch, path) var blob []byte if err == nil { diff --git a/handlers/repo-refs.go b/handlers/repo-refs.go index c1677db..222d1ef 100644 --- a/handlers/repo-refs.go +++ b/handlers/repo-refs.go @@ -19,7 +19,20 @@ func NewRepoRefs(cfg *config.Global, url url.Reverser, repo models.Repo, consume } func (rr *RepoRefs) Serve(r Request) { - err := rr.consumer.Consume(r, ctx.NewRepoRefs(rr.cfg, rr.url, rr.repo)) + branch := r.QueryString()["h"] + + if branch == "" { + branch = rr.repo.DefaultBranch() + } + + branches, err := rr.repo.Branches() + + if err != nil { + r.Error(err) + return + } + + err = rr.consumer.Consume(r, ctx.NewRepoRefs(rr.cfg, rr.url, rr.repo, branch, branches)) if err != nil { r.Error(err) diff --git a/handlers/repo-root.go b/handlers/repo-root.go index cb5dd80..cee5595 100644 --- a/handlers/repo-root.go +++ b/handlers/repo-root.go @@ -20,7 +20,20 @@ func NewRepoRoot(cfg *config.Global, url url.Reverser, repo models.Repo, consume } func (rr *RepoRoot) Serve(r Request) { - file, err := rr.repo.Find(rr.repo.DefaultBranch(), "/") + branch := r.QueryString()["h"] + + if branch == "" { + branch = rr.repo.DefaultBranch() + } + + branches, err := rr.repo.Branches() + + if err != nil { + r.Error(err) + return + } + + file, err := rr.repo.Find(branch, "/") var files []models.RepoFile if err == nil { @@ -28,9 +41,9 @@ func (rr *RepoRoot) Serve(r Request) { } if err == nil { - readme, isReadmeHTML := rr.Readme() - ctx := ctx.NewRepoRoot(rr.cfg, rr.url, rr.repo, files, readme, - isReadmeHTML) + readme, isReadmeHTML := rr.Readme(branch) + ctx := ctx.NewRepoRoot(rr.cfg, rr.url, rr.repo, branch, branches, + files, readme, isReadmeHTML) err = rr.consumer.Consume(r, ctx) } @@ -39,8 +52,8 @@ func (rr *RepoRoot) Serve(r Request) { } } -func (rr *RepoRoot) Readme() (content string, isReadmeHTML bool) { - readme := rr.repo.Readme(rr.repo.DefaultBranch()) +func (rr *RepoRoot) Readme(branch string) (content string, isReadmeHTML bool) { + readme := rr.repo.Readme(branch) if readme != nil { if readme.Type == models.RepoReadmeTypeMarkdown { diff --git a/handlers/repo-tree.go b/handlers/repo-tree.go index abc98d3..be6d40b 100644 --- a/handlers/repo-tree.go +++ b/handlers/repo-tree.go @@ -21,8 +21,21 @@ func NewRepoTree(cfg *config.Global, url url.Reverser, repo models.Repo, consume } func (rt *RepoTree) Serve(r Request) { + branch := r.QueryString()["h"] + + if branch == "" { + branch = rt.repo.DefaultBranch() + } + + branches, err := rt.repo.Branches() + + if err != nil { + r.Error(err) + return + } + path := r.Subtree() - file, err := rt.repo.Find(rt.repo.DefaultBranch(), path) + file, err := rt.repo.Find(branch, path) if err != nil { r.Error(err) @@ -63,7 +76,8 @@ func (rt *RepoTree) Serve(r Request) { } if err == nil { - ctx := ctx.NewRepoTree(rt.cfg, rt.url, rt.repo, path, file.IsDir(), isBinary, isImage, files, blob) + ctx := ctx.NewRepoTree(rt.cfg, rt.url, rt.repo, branch, branches, path, + file.IsDir(), isBinary, isImage, files, blob) err = rt.consumer.Consume(r, ctx) } diff --git a/models/repo.go b/models/repo.go index 465a3bc..65fd742 100644 --- a/models/repo.go +++ b/models/repo.go @@ -14,6 +14,7 @@ type Repo interface { Description() string Path() string DefaultBranch() string + Branches() ([]string, error) // Actions Open() error @@ -44,6 +45,34 @@ func (r *repo) Open() error { return err } +func (r *repo) Branches() (branches []string, err error) { + iterator, err := r.repo.NewBranchIterator(git.BranchLocal) + + if err != nil { + return + } + + err = iterator.ForEach(func(branch *git.Branch, _ git.BranchType) (err error) { + name, err := branch.Name() + + if err != nil { + return + } + + branches = append(branches, name) + return + }) + + iterator.Free() + + // On error, return a nil slice + if err != nil { + branches = nil + } + + return +} + func (r *repo) Find(branch, path string) (f RepoFile, err error) { ref, err := r.repo.LookupBranch(branch, git.BranchLocal) diff --git a/routers/main.go b/routers/main.go index 70ffed6..0c6fa2c 100644 --- a/routers/main.go +++ b/routers/main.go @@ -61,20 +61,31 @@ func (m *main) ServeHTTP(w http.ResponseWriter, r *http.Request) { } type handlerRequest struct { - writer http.ResponseWriter - request *http.Request - subtree string - written bool + writer http.ResponseWriter + request *http.Request + subtree string + written bool + querystring map[string]string } func newHandlerRequest(writer http.ResponseWriter, request *http.Request, registeredPath string) *handlerRequest { subtree := request.URL.Path[len(registeredPath)-1:] - return &handlerRequest{writer, request, subtree, false} + // Build the querystring map. For now, discard all but the last value + // provided for a key + querystring := make(map[string]string) + for key, values := range request.URL.Query() { + if len(values) > 0 { + querystring[key] = values[len(values)-1] + } + } + + return &handlerRequest{writer, request, subtree, false, querystring} } -func (hr *handlerRequest) Path() string { return hr.request.URL.Path } -func (hr *handlerRequest) Subtree() string { return hr.subtree } +func (hr *handlerRequest) Path() string { return hr.request.URL.Path } +func (hr *handlerRequest) Subtree() string { return hr.subtree } +func (hr *handlerRequest) QueryString() map[string]string { return hr.querystring } func (hr *handlerRequest) Write(data []byte) (int, error) { hr.written = true return hr.writer.Write(data) diff --git a/routers/repo.go b/routers/repo.go index 650a989..cb86399 100644 --- a/routers/repo.go +++ b/routers/repo.go @@ -6,6 +6,7 @@ import ( "code.austinjadams.com/gong/models" "code.austinjadams.com/gong/templates" "code.austinjadams.com/gong/templates/url" + url_ "net/url" ) type Repo struct { @@ -20,15 +21,15 @@ func NewRepo(cfg *config.Global, url url.Reverser, repo models.Repo, templates t } func (r *Repo) ConfigureRouter(superRouter Router) { - superRouter.Handle(r.url.RepoRoot(r.repo), false, + superRouter.Handle(r.url.RepoRoot(r.repo, ""), false, handlers.NewRepoRoot(r.cfg, r.url, r.repo, r.templates.Consumer("repo-root"))) - superRouter.Handle(r.url.RepoPlain(r.repo, "/"), true, + superRouter.Handle(r.url.RepoPlain(r.repo, "", "/"), true, handlers.NewRepoPlain(r.url, r.repo)) - superRouter.Handle(r.url.RepoTree(r.repo, "/", true), true, + superRouter.Handle(r.url.RepoTree(r.repo, "", "/", true), true, handlers.NewRepoTree(r.cfg, r.url, r.repo, r.templates.Consumer("repo-tree"))) - superRouter.Handle(r.url.RepoLog(r.repo), false, + superRouter.Handle(r.url.RepoLog(r.repo, ""), false, handlers.NewRepoLog(r.cfg, r.url, r.repo, r.templates.Consumer("repo-log"))) - superRouter.Handle(r.url.RepoRefs(r.repo), false, + superRouter.Handle(r.url.RepoRefs(r.repo, ""), false, handlers.NewRepoRefs(r.cfg, r.url, r.repo, r.templates.Consumer("repo-refs"))) } @@ -40,16 +41,36 @@ func NewRepoReverser(repoPrefix string) url.RepoReverser { return &repoReverser{repoPrefix} } -func (r *repoReverser) RepoRoot(repo models.Repo) string { +func (r *repoReverser) buildQueryString(repo models.Repo, branch string) string { + if branch == "" { + branch = repo.DefaultBranch() + } + + if branch == repo.DefaultBranch() { + return "" + } else { + return "?h=" + url_.QueryEscape(branch) + } +} + +func (r *repoReverser) repoRoot(repo models.Repo, branch string) string { return r.repoPrefix + "/" + repo.Name() + "/" } -func (r *repoReverser) RepoPlain(repo models.Repo, path string) string { - return r.RepoRoot(repo) + "plain" + path +func (r *repoReverser) RepoRoot(repo models.Repo, branch string) string { + return r.repoRoot(repo, branch) + r.buildQueryString(repo, branch) +} + +func (r *repoReverser) repoPlain(repo models.Repo, branch string, path string) string { + return r.repoRoot(repo, branch) + "plain" + path } -func (r *repoReverser) RepoTree(repo models.Repo, path string, isDir bool) string { - result := r.RepoRoot(repo) + "tree" + path +func (r *repoReverser) RepoPlain(repo models.Repo, branch string, path string) string { + return r.repoPlain(repo, branch, path) + r.buildQueryString(repo, branch) +} + +func (r *repoReverser) repoTree(repo models.Repo, branch string, path string, isDir bool) string { + result := r.repoRoot(repo, branch) + "tree" + path hasSlash := result[len(result)-1] == '/' // Remove/add slash as needed @@ -62,10 +83,22 @@ func (r *repoReverser) RepoTree(repo models.Repo, path string, isDir bool) strin return result } -func (r *repoReverser) RepoLog(repo models.Repo) string { - return r.RepoRoot(repo) + "log/" +func (r *repoReverser) RepoTree(repo models.Repo, branch string, path string, isDir bool) string { + return r.repoTree(repo, branch, path, isDir) + r.buildQueryString(repo, branch) +} + +func (r *repoReverser) repoLog(repo models.Repo, branch string) string { + return r.repoRoot(repo, branch) + "log/" +} + +func (r *repoReverser) RepoLog(repo models.Repo, branch string) string { + return r.repoLog(repo, branch) + r.buildQueryString(repo, branch) +} + +func (r *repoReverser) repoRefs(repo models.Repo, branch string) string { + return r.repoRoot(repo, branch) + "refs/" } -func (r *repoReverser) RepoRefs(repo models.Repo) string { - return r.RepoRoot(repo) + "refs/" +func (r *repoReverser) RepoRefs(repo models.Repo, branch string) string { + return r.repoRefs(repo, branch) + r.buildQueryString(repo, branch) } diff --git a/static/style.css b/static/style.css index bb10f6f..a53f6d5 100644 --- a/static/style.css +++ b/static/style.css @@ -70,6 +70,23 @@ header nav a:hover { padding: 0; } +#branch-form select { + background-color: transparent; + border: 1px solid #aaa; + display: inline-block; + margin: 8px auto 4px auto; + font-size: 14px; + text-align: center; +} + +#branch-form option { + text-align: left; +} + +#branch-form option[selected] { + font-weight: bold; +} + #container { margin: 0 auto; max-width: 720px; diff --git a/templates/ctx/repo-log.go b/templates/ctx/repo-log.go index 8d288a9..58bac20 100644 --- a/templates/ctx/repo-log.go +++ b/templates/ctx/repo-log.go @@ -10,8 +10,8 @@ type RepoLog interface { RepoGlobal } -func NewRepoLog(cfg *config.Global, url url.Reverser, repo models.Repo) RepoLog { - return &repoLog{NewRepoGlobal(cfg, url, repo)} +func NewRepoLog(cfg *config.Global, url url.Reverser, repo models.Repo, branch string, branches []string) RepoLog { + return &repoLog{NewRepoGlobal(cfg, url, repo, branch, branches)} } type repoLog struct { diff --git a/templates/ctx/repo-refs.go b/templates/ctx/repo-refs.go index 62bfcad..1061290 100644 --- a/templates/ctx/repo-refs.go +++ b/templates/ctx/repo-refs.go @@ -10,8 +10,8 @@ type RepoRefs interface { RepoGlobal } -func NewRepoRefs(cfg *config.Global, url url.Reverser, repo models.Repo) RepoRefs { - return &repoRefs{NewRepoGlobal(cfg, url, repo)} +func NewRepoRefs(cfg *config.Global, url url.Reverser, repo models.Repo, branch string, branches []string) RepoRefs { + return &repoRefs{NewRepoGlobal(cfg, url, repo, branch, branches)} } type repoRefs struct { diff --git a/templates/ctx/repo-root.go b/templates/ctx/repo-root.go index 60818bb..aeae819 100644 --- a/templates/ctx/repo-root.go +++ b/templates/ctx/repo-root.go @@ -14,8 +14,8 @@ type RepoRoot interface { ReadmeHTML() template.HTML } -func NewRepoRoot(cfg *config.Global, url url.Reverser, repo models.Repo, files []models.RepoFile, readme string, isReadmeHTML bool) RepoRoot { - return &repoRoot{NewRepoTree(cfg, url, repo, "/", true, false, false, files, ""), readme, isReadmeHTML} +func NewRepoRoot(cfg *config.Global, url url.Reverser, repo models.Repo, branch string, branches []string, files []models.RepoFile, readme string, isReadmeHTML bool) RepoRoot { + return &repoRoot{NewRepoTree(cfg, url, repo, branch, branches, "/", true, false, false, files, ""), readme, isReadmeHTML} } type repoRoot struct { diff --git a/templates/ctx/repo-tree.go b/templates/ctx/repo-tree.go index ec82ca2..95f1409 100644 --- a/templates/ctx/repo-tree.go +++ b/templates/ctx/repo-tree.go @@ -20,8 +20,8 @@ type RepoTree interface { Blob() string } -func NewRepoTree(cfg *config.Global, url url.Reverser, repo models.Repo, path string, isListing bool, isBinary bool, isImage bool, files []models.RepoFile, blob string) RepoTree { - return &repoTree{NewRepoGlobal(cfg, url, repo), path, NewPath(path), isListing, isBinary, isImage, files, blob} +func NewRepoTree(cfg *config.Global, url url.Reverser, repo models.Repo, branch string, branches []string, path string, isListing bool, isBinary bool, isImage bool, files []models.RepoFile, blob string) RepoTree { + return &repoTree{NewRepoGlobal(cfg, url, repo, branch, branches), path, NewPath(path), isListing, isBinary, isImage, files, blob} } type repoTree struct { diff --git a/templates/ctx/repo.go b/templates/ctx/repo.go index 2ee06ff..39b11b0 100644 --- a/templates/ctx/repo.go +++ b/templates/ctx/repo.go @@ -12,19 +12,25 @@ type RepoGlobal interface { Global Repo() models.Repo + Branch() string + Branches() []string } -func NewRepoGlobal(cfg *config.Global, url url.Reverser, repo models.Repo) RepoGlobal { - return &repoGlobal{NewGlobal(cfg, url), repo} +func NewRepoGlobal(cfg *config.Global, url url.Reverser, repo models.Repo, branch string, branches []string) RepoGlobal { + return &repoGlobal{NewGlobal(cfg, url), repo, branch, branches} } type repoGlobal struct { Global - repo models.Repo + repo models.Repo + branch string + branches []string } -func (r *repoGlobal) Repo() models.Repo { return r.repo } +func (r *repoGlobal) Repo() models.Repo { return r.repo } +func (r *repoGlobal) Branch() string { return r.branch } +func (r *repoGlobal) Branches() []string { return r.branches } func (r *repoGlobal) Equals(other Global) bool { otherGlobal, ok := other.(RepoGlobal) diff --git a/templates/list.html b/templates/list.html index e751f20..40c8359 100644 --- a/templates/list.html +++ b/templates/list.html @@ -6,7 +6,7 @@ </header> <ul id="list"> {{ range .Repos }} - <li><a href="{{ $.URL.RepoRoot . }}"> + <li><a href="{{ $.URL.RepoRoot . "" }}"> <div class="list-name">{{ .Name }}</div> {{ with .Description }}<div class="list-description">{{ . }}</div>{{ end }} </a></li> diff --git a/templates/repo-tree.html b/templates/repo-tree.html index 97f08cd..ce57961 100644 --- a/templates/repo-tree.html +++ b/templates/repo-tree.html @@ -4,16 +4,16 @@ tree. Breadcrumbs consisting of just `root' aren't helpful. */}} {{ if .SplitPath }} <div id="breadcrumbs"> - <a href="{{ .URL.RepoTree .Repo "/" true }}">root</a>{{ range .SplitPath }}/{{ if .IsLast }}<strong>{{ .Name }}</strong>{{ else }}<a href="{{ $.URL.RepoTree $.Repo .FullPath true }}">{{ .Name }}</a>{{ end }}{{ end }} + <a href="{{ .URL.RepoTree .Repo .Branch "/" true }}">root</a>{{ range .SplitPath }}/{{ if .IsLast }}<strong>{{ .Name }}</strong>{{ else }}<a href="{{ $.URL.RepoTree $.Repo $.Branch .FullPath true }}">{{ .Name }}</a>{{ end }}{{ end }} </div> {{ end }} {{ if .IsListing }} <table id="files"> {{ if not .IsRoot }} - <tr><td class="filemode">d---------</td><td class="filename filedir"><a href="{{ .URL.RepoTree .Repo .Dirname true }}">..</a></td></tr> + <tr><td class="filemode">d---------</td><td class="filename filedir"><a href="{{ .URL.RepoTree .Repo .Branch .Dirname true }}">..</a></td></tr> {{ end }} {{ range .Files }} - <tr><td class="filemode">{{ if .IsDir }}d{{ else }}-{{ end }}{{ if .IsExecutable }}rwxr-xr-x{{ else }}rw-r--r--{{ end }}</td><td class="filename {{ if .IsDir }}filedir{{ end }}"><a href="{{ $.URL.RepoTree $.Repo (print $.Path .Name) .IsDir }}">{{ .Name }}</a><td class="filesize">{{ if .IsFile }}{{ .Size }}{{ end }}</td></tr> + <tr><td class="filemode">{{ if .IsDir }}d{{ else }}-{{ end }}{{ if .IsExecutable }}rwxr-xr-x{{ else }}rw-r--r--{{ end }}</td><td class="filename {{ if .IsDir }}filedir{{ end }}"><a href="{{ $.URL.RepoTree $.Repo $.Branch (print $.Path .Name) .IsDir }}">{{ .Name }}</a><td class="filesize">{{ if .IsFile }}{{ .Size }}{{ end }}</td></tr> {{ end }} </table> {{ end }} @@ -22,7 +22,7 @@ {{ if not .IsListing }} {{ if .IsBinary }} {{ if .IsImage }} - <img src="{{ .URL.RepoPlain .Repo .Path }}" alt="image: {{ .Path }}" /> + <img src="{{ .URL.RepoPlain .Repo .Branch .Path }}" alt="image: {{ .Path }}" /> {{ else }} <p>don't know how to display binary data yet, sorry</p> {{ end }} diff --git a/templates/repo.html b/templates/repo.html index 761a278..0d09fad 100644 --- a/templates/repo.html +++ b/templates/repo.html @@ -1,10 +1,20 @@ {{ define "title" }}{{ block "repo-title" . }}{{ end }} « {{ .Repo.Name }}{{ end }} {{ define "content" }} <header> - <h1 id="title"><a href="{{ .URL.RepoRoot .Repo }}">{{ .Repo.Name }}</a></h1> + <h1 id="title"><a href="{{ .URL.RepoRoot .Repo .Branch }}">{{ .Repo.Name }}</a></h1> <p id="subtitle">{{ .Repo.Description }}</p> + <form id="branch-form"> + <select name="h" id="branch-select" oninput="this.form.submit()"> + {{ range .Branches }} + <option value="{{ . }}"{{ if eq . $.Branch }} selected{{ end }}>{{ . }}</option> + {{ end }} + </select> + <noscript> + <input type="submit" value="→"></input> + </noscript> + </form> <nav> - <ul><li><a href="{{ .URL.RepoTree .Repo "/" true }}">browse</a></li><li><a href="{{ .URL.RepoLog .Repo }}">log</a></li><li><a href="{{ .URL.RepoRefs .Repo }}">releases</a></li></ul> + <ul><li><a href="{{ .URL.RepoTree .Repo .Branch "/" true }}">browse</a></li><li><a href="{{ .URL.RepoLog .Repo .Branch }}">log</a></li><li><a href="{{ .URL.RepoRefs .Repo .Branch }}">releases</a></li></ul> </nav> {{ block "repo-header" . }}{{ end }} </header> diff --git a/templates/url/reverser.go b/templates/url/reverser.go index 2e6f69d..77f7780 100644 --- a/templates/url/reverser.go +++ b/templates/url/reverser.go @@ -12,9 +12,9 @@ type Reverser interface { } type RepoReverser interface { - RepoRoot(repo models.Repo) string - RepoPlain(repo models.Repo, path string) string - RepoTree(repo models.Repo, path string, isDir bool) string - RepoLog(repo models.Repo) string - RepoRefs(repo models.Repo) string + RepoRoot(repo models.Repo, branch string) string + RepoPlain(repo models.Repo, branch string, path string) string + RepoTree(repo models.Repo, branch string, path string, isDir bool) string + RepoLog(repo models.Repo, branch string) string + RepoRefs(repo models.Repo, branch string) string } |