[OCaml] Mobile-friendly clone of cgit.
Modularize everything.
Changed files
lib/git_helpers.ml
@@ -4,13 +4,6 @@
4
4
5
5
type user_record = { name : string; email : string }
6
6
7
Removed:
type commit_record = {
8
Removed:
hash : string;
9
Removed:
parents : string list;
10
Removed:
author : Git.User.t;
11
Removed:
message : string option;
12
Removed:
}
13
Removed:
14
7
let full_path path = Filename.concat Config.git_directory path
15
8
16
9
let store repo_path =
@@ -23,39 +16,51 @@
23
16
let description_path = Filename.concat (full_path repo_path) "description" in
24
17
In_channel.with_open_text description_path In_channel.input_all
25
18
26
Removed:
(* Read a Git object and turn it into our [commit_record], or propagate an error. *)
27
Removed:
let get_commit_record store h =
28
Removed:
Store.read store h >>= function
29
Removed:
| Ok (Value.Commit c) ->
30
Removed:
Lwt.return_ok
31
Removed:
{
32
Removed:
hash = Store.Hash.to_hex h;
33
Removed:
parents = Store.Value.Commit.parents c |> List.map Store.Hash.to_hex;
34
Removed:
author = Store.Value.Commit.author c;
35
Removed:
message = Store.Value.Commit.message c;
36
Removed:
}
37
Removed:
| Ok _ -> Lwt.return_error (`Msg "object is not a commit")
38
Removed:
| Error e -> Lwt.return_error e
19
Added:
module Commit = struct
20
Added:
type t = {
21
Added:
hash : string;
22
Added:
parents : string list;
23
Added:
author : Git.User.t;
24
Added:
message : string option;
25
Added:
}
39
26
40
Removed:
let recent_commits repo_path n =
41
Removed:
let open Lwt_result.Syntax in
42
Removed:
let* store = store repo_path in
43
Removed:
let* head = Store.Ref.resolve store Git.Reference.head in
44
Removed:
let rec walk acc hash count =
45
Removed:
if count = 0 then Lwt.return_ok (List.rev acc)
46
Removed:
else
47
Removed:
get_commit_record store hash >>= function
48
Removed:
| Error e -> Lwt.return_error e
49
Removed:
| Ok commit -> (
50
Removed:
match commit.parents with
51
Removed:
| parent :: _ ->
52
Removed:
walk (commit :: acc) (Store.Hash.of_hex parent) (count - 1)
53
Removed:
| [] -> Lwt.return_ok (List.rev (commit :: acc)))
54
Removed:
in
55
Removed:
walk [] head n
27
Added:
let of_hash store h =
28
Added:
Store.read store h >>= function
29
Added:
| Ok (Value.Commit c) ->
30
Added:
Lwt_result.return
31
Added:
{
32
Added:
hash = Store.Hash.to_hex h;
33
Added:
parents = Store.Value.Commit.parents c |> List.map Store.Hash.to_hex;
34
Added:
author = Store.Value.Commit.author c;
35
Added:
message = Store.Value.Commit.message c;
36
Added:
}
37
Added:
| Ok _ -> Lwt.return_error (`Msg "object is not a commit")
38
Added:
| Error e -> Lwt.return_error e
56
39
57
Removed:
let get_commit repo_path id =
58
Removed:
let open Lwt_result.Syntax in
59
Removed:
let* store = store repo_path in
60
Removed:
let id = Store.Hash.of_hex id in
61
Removed:
get_commit_record store id
40
Added:
let recent_commits repo_path n =
41
Added:
let open Lwt_result.Syntax in
42
Added:
let* store = store repo_path in
43
Added:
let* head = Store.Ref.resolve store Git.Reference.head in
44
Added:
let rec walk acc hash count =
45
Added:
if count = 0 then Lwt_result.return (List.rev acc)
46
Added:
else
47
Added:
of_hash store hash >>= function
48
Added:
| Error e -> Lwt.return_error e
49
Added:
| Ok commit -> (
50
Added:
match commit.parents with
51
Added:
| parent :: _ ->
52
Added:
walk (commit :: acc) (Store.Hash.of_hex parent) (count - 1)
53
Added:
| [] -> Lwt_result.return (List.rev (commit :: acc)))
54
Added:
in
55
Added:
walk [] head n
56
Added:
57
Added:
let of_id repo_path id =
58
Added:
let open Lwt_result.Syntax in
59
Added:
let* store = store repo_path in
60
Added:
let id = Store.Hash.of_hex id in
61
Added:
of_hash store id
62
Added:
end
63
Added:
64
Added:
module Branch = struct
65
Added:
let all_branches repo_path = [ "foo"; "bar"; repo_path ]
66
Added:
end
lib/handlers.ml
@@ -6,41 +6,39 @@
6
6
let msg = Format.asprintf "%a" Git_unix.Store.pp_error e in
7
7
Views.error_page msg |> Dream_html.respond
8
8
9
Removed:
let ogit_root _req = Views.ogit_root () |> Dream_html.respond
9
Added:
let root _req = Views.root () |> Dream_html.respond
10
10
11
Removed:
let repo_summary req =
12
Removed:
let repo_path = Dream.param req "repo_name" in
13
Removed:
let branches = [ "bing"; "bong" ] in
14
Removed:
let* commits = Git_helpers.recent_commits repo_path 10 in
15
Removed:
Views.repo_summary repo_path branches commits |> Dream_html.respond
11
Added:
module Repo = struct
12
Added:
let repo_path req = Dream.param req "repo_name"
16
13
17
Removed:
let repo_commit req =
18
Removed:
let repo_path = Dream.param req "repo_name" in
19
Removed:
let id = match Dream.query req "id" with Some id -> id | None -> "" in
20
Removed:
let* commit = Git_helpers.get_commit repo_path id in
21
Removed:
Views.repo_commit repo_path commit |> Dream_html.respond
14
Added:
let summary req =
15
Added:
let branches = Git_helpers.Branch.all_branches (repo_path req) in
16
Added:
let* commits = Git_helpers.Commit.recent_commits (repo_path req) 10 in
17
Added:
let authors = [ "John Pork"; "Sebastian Jellybean" ] in
18
Added:
Views.Repo.summary (repo_path req) branches commits authors
19
Added:
|> Dream_html.respond
22
20
23
Removed:
(* let repo_tree req = *)
24
Removed:
(* let repo_name = Dream.param req "repo_name" in *)
25
Removed:
(* let path = Git_helpers.full_path repo_name in *)
26
Removed:
(* let dir_path = Dream.target req in *)
27
Removed:
(* Views.repo_tree ~repo_path:path dir_path |> Dream_html.respond *)
21
Added:
let refs req = Views.Repo.refs (repo_path req) |> Dream_html.respond
22
Added:
let log req = Views.Repo.log (repo_path req) |> Dream_html.respond
23
Added:
let tree req = Views.Repo.tree (repo_path req) |> Dream_html.respond
28
24
29
Removed:
(* let repo_blob req = *)
30
Removed:
(* let repo_name = Dream.param req "repo_name" in *)
31
Removed:
(* let path = Git_helpers.full_path repo_name in *)
32
Removed:
(* let blob_path = Dream.query req "path" |> Option.value ~default:"" in *)
33
Removed:
(* Views.repo_blob repo_name blob_path |> Dream_html.respond *)
25
Added:
let commit req =
26
Added:
let id = match Dream.query req "id" with Some id -> id | None -> "" in
27
Added:
let* commit = Git_helpers.Commit.of_id (repo_path req) id in
28
Added:
Views.Repo.commit (repo_path req) commit |> Dream_html.respond
29
Added:
end
34
30
35
31
let all_handlers =
36
32
[
37
Removed:
Dream.get "/" ogit_root;
33
Added:
Dream.get "/" root;
38
34
Dream.scope "/:repo_name" []
39
Removed:
[
40
Removed:
Dream.get "/" repo_summary;
41
Removed:
Dream.get "/commit/" repo_commit;
42
Removed:
(* Dream.get "/tree" repo_tree; *)
43
Removed:
(* Dream.get "/blob" repo_blob; *)
44
Removed:
];
35
Added:
Repo.
36
Added:
[
37
Added:
Dream.get "/" summary;
38
Added:
Dream.get "/refs/" refs;
39
Added:
Dream.get "/log/" log;
40
Added:
Dream.get "/tree/" tree;
41
Added:
Dream.get "/commit/" commit;
42
Added:
];
45
43
Dream.get "/static/**" (Dream.static "./lib/static");
46
44
]
lib/static/styles.css
@@ -69,3 +69,10 @@
69
69
color: black;
70
70
text-decoration: revert;
71
71
}
72
Added:
73
Added:
h1 {
74
Added:
position: sticky;
75
Added:
top: 0;
76
Added:
background: inherit;
77
Added:
padding: 0.5em 0;
78
Added:
}
lib/views.ml
@@ -3,12 +3,40 @@
3
3
type body_data = {
4
4
title : string;
5
5
subtitle : string;
6
Removed:
topnav : Dream_html.node;
7
6
content : Dream_html.node list;
8
7
}
9
8
10
Removed:
module Layout = struct
9
Added:
module Components = struct
11
10
open Dream_html
11
Added:
12
Added:
let topnav current_path =
13
Added:
let open HTML in
14
Added:
let li_of_a (path, text) =
15
Added:
let is_active = path = current_path in
16
Added:
let attrs = if is_active then [ id "active" ] else [] in
17
Added:
let url =
18
Added:
if String.equal path "/" then Printf.sprintf "/%s/" current_path
19
Added:
else Printf.sprintf "/%s/%s" current_path path
20
Added:
in
21
Added:
li attrs [ a [ href "%s" url ] [ txt text ] ]
22
Added:
in
23
Added:
nav
24
Added:
[ id "top" ]
25
Added:
[
26
Added:
ul []
27
Added:
@@ List.map li_of_a
28
Added:
[
29
Added:
("/", "summary");
30
Added:
("refs/", "refs");
31
Added:
("log/", "log");
32
Added:
("tree/", "tree");
33
Added:
("commit/", "commit");
34
Added:
];
35
Added:
]
36
Added:
end
37
Added:
38
Added:
module Page = struct
39
Added:
open Dream_html
12
40
open HTML
13
41
14
42
let header title subtitle =
@@ -22,7 +50,7 @@
22
50
23
51
let default_head_data = { page_title = "Ogit" }
24
52
25
Removed:
let application ?(head_data = default_head_data) body_data =
53
Added:
let render ?(head_data = default_head_data) body_data =
26
54
html []
27
55
[
28
56
head []
@@ -35,44 +63,14 @@
35
63
body []
36
64
[
37
65
header body_data.title body_data.subtitle;
38
Removed:
body_data.topnav;
66
Added:
Components.topnav body_data.title;
39
67
div [ id "main" ] body_data.content;
40
68
footer ();
41
69
];
42
70
]
43
71
end
44
72
45
Removed:
module Components = struct
46
Removed:
open Dream_html
47
Removed:
48
Removed:
let topnav repo_path current_path =
49
Removed:
let open HTML in
50
Removed:
let li_of_a (path, text) =
51
Removed:
let is_active = path = current_path in
52
Removed:
let attrs = if is_active then [ id "active" ] else [] in
53
Removed:
let url =
54
Removed:
if String.equal path "/" then Printf.sprintf "/%s/" repo_path
55
Removed:
else Printf.sprintf "/%s/%s" repo_path path
56
Removed:
in
57
Removed:
li attrs [ a [ href "%s" url ] [ txt text ] ]
58
Removed:
in
59
Removed:
nav
60
Removed:
[ id "top" ]
61
Removed:
[
62
Removed:
ul []
63
Removed:
@@ List.map li_of_a
64
Removed:
[
65
Removed:
("/", "summary");
66
Removed:
("refs", "refs");
67
Removed:
("log", "log");
68
Removed:
("tree", "tree");
69
Removed:
("commit", "commit");
70
Removed:
("diff", "diff");
71
Removed:
];
72
Removed:
]
73
Removed:
end
74
Removed:
75
Removed:
let ogit_root () =
73
Added:
let root () =
76
74
let open Dream_html in
77
75
let repositories_in directory =
78
76
let repos =
@@ -87,61 +85,89 @@
87
85
{
88
86
title = "Ogit";
89
87
subtitle = "Repositories for " ^ Config.author;
90
Removed:
topnav = HTML.(null []);
91
88
content = [ repositories_in Config.git_directory ];
92
89
}
93
90
in
94
Removed:
Layout.application body_data
91
Added:
Page.render body_data
95
92
96
Removed:
let repo_summary repo_path branches commits =
97
Removed:
let open Git_helpers in
98
Removed:
let open Dream_html in
99
Removed:
let li_of_branch branch =
100
Removed:
HTML.(li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ])
101
Removed:
in
102
Removed:
let li_of_commit commit =
103
Removed:
match commit.message with
104
Removed:
| Some msg ->
105
Removed:
HTML.(
106
Removed:
li []
107
Removed:
[
108
Removed:
a
109
Removed:
[ href "commit/?id=%s" commit.hash ]
110
Removed:
[ txt "%s %s" (short_hash commit.hash) msg ];
111
Removed:
])
112
Removed:
| None -> HTML.(li [] [ a [ href "" ] [ txt "caca!!" ] ])
113
Removed:
in
114
Removed:
let content =
115
Removed:
HTML.
116
Removed:
[
117
Removed:
h3 [] [ txt "Branches" ];
118
Removed:
ul [] (List.map li_of_branch branches);
119
Removed:
h3 [] [ txt "Recent commits" ];
120
Removed:
ul [] (List.map li_of_commit commits);
121
Removed:
]
122
Removed:
in
123
Removed:
Layout.application
124
Removed:
{
125
Removed:
title = repo_path;
126
Removed:
subtitle = repo_description repo_path;
127
Removed:
topnav = Components.topnav repo_path "";
128
Removed:
content;
129
Removed:
}
93
Added:
module Repo = struct
94
Added:
let summary repo_path branches commits authors =
95
Added:
let open Dream_html in
96
Added:
let li_of_branch branch =
97
Added:
HTML.(li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ])
98
Added:
in
99
Added:
let li_of_commit (commit : Git_helpers.Commit.t) =
100
Added:
match commit.message with
101
Added:
| Some msg ->
102
Added:
HTML.(
103
Added:
li []
104
Added:
[
105
Added:
a
106
Added:
[ href "commit/?id=%s" commit.hash ]
107
Added:
[ txt "%s %s" (Git_helpers.short_hash commit.hash) msg ];
108
Added:
])
109
Added:
| None -> HTML.(li [] [ a [ href "" ] [ txt "caca!!" ] ])
110
Added:
in
111
Added:
let li_of_author author =
112
Added:
HTML.(li [] [ a [ href "" ] [ txt "%s" author ] ])
113
Added:
in
114
Added:
let content =
115
Added:
HTML.
116
Added:
[
117
Added:
h3 [] [ txt "Branches" ];
118
Added:
ul [] (List.map li_of_branch branches);
119
Added:
h3 [] [ txt "Recent commits" ];
120
Added:
ul [] (List.map li_of_commit commits);
121
Added:
h3 [] [ txt "Authors" ];
122
Added:
ul [] (List.map li_of_author authors);
123
Added:
]
124
Added:
in
125
Added:
Page.render
126
Added:
{
127
Added:
title = repo_path;
128
Added:
subtitle = Git_helpers.repo_description repo_path;
129
Added:
content;
130
Added:
}
130
131
131
Removed:
let repo_commit repo_path commit =
132
Removed:
let open Git_helpers in
133
Removed:
let open Dream_html in
134
Removed:
let message = match commit.message with Some msg -> msg | None -> "" in
135
Removed:
let content = HTML.[ h3 [] [ txt "%s" message ] ] in
136
Removed:
let title = Printf.sprintf "%s : %s" repo_path (short_hash commit.hash) in
137
Removed:
Layout.application
138
Removed:
{
139
Removed:
title;
140
Removed:
subtitle = "";
141
Removed:
topnav = Components.topnav repo_path "";
142
Removed:
content;
143
Removed:
}
132
Added:
let refs repo_path =
133
Added:
let open Dream_html in
134
Added:
Page.render
135
Added:
HTML.
136
Added:
{
137
Added:
title = repo_path;
138
Added:
subtitle = Git_helpers.repo_description repo_path;
139
Added:
content = [ null [] ];
140
Added:
}
144
141
142
Added:
let log repo_path =
143
Added:
let open Dream_html in
144
Added:
Page.render
145
Added:
HTML.
146
Added:
{
147
Added:
title = repo_path;
148
Added:
subtitle = Git_helpers.repo_description repo_path;
149
Added:
content = [ null [] ];
150
Added:
}
151
Added:
152
Added:
let tree repo_path =
153
Added:
let open Dream_html in
154
Added:
Page.render
155
Added:
HTML.
156
Added:
{
157
Added:
title = repo_path;
158
Added:
subtitle = Git_helpers.repo_description repo_path;
159
Added:
content = [ null [] ];
160
Added:
}
161
Added:
162
Added:
let commit repo_path (commit : Git_helpers.Commit.t) =
163
Added:
let open Dream_html in
164
Added:
let open Git_helpers in
165
Added:
let message = match commit.message with Some msg -> msg | None -> "" in
166
Added:
let content = HTML.[ h3 [] [ txt "%s" message ] ] in
167
Added:
let title = Printf.sprintf "%s : %s" repo_path (short_hash commit.hash) in
168
Added:
Page.render { title; subtitle = ""; content }
169
Added:
end
170
Added:
145
171
let error_page message =
146
172
let open Dream_html in
147
173
HTML.(
@@ -149,19 +175,24 @@
149
175
[
150
176
head []
151
177
[
152
Removed:
title [] "Big error";
178
Added:
title [] "Fatal Error";
153
179
link [ rel "stylesheet"; href "/static/styles.css" ];
154
180
link
155
181
[ rel "icon"; type_ "image/x-icon"; href "/static/git_icon.svg" ];
156
182
];
157
183
body []
158
184
[
159
Removed:
h1 [] [ txt "Major error alert" ];
160
Removed:
h2 [] [ txt "Major alert subtitle" ];
161
Removed:
(* Components.topnav; *)
162
Removed:
div [ id "main" ] [ txt "%s" message ];
185
Added:
h1 [] [ txt "Fatal Error" ];
186
Added:
div
187
Added:
[ id "main" ]
188
Added:
[
189
Added:
p [] [ b [] [ txt "%s" message ] ];
190
Added:
p []
191
Added:
[
192
Added:
txt
193
Added:
"Your best course of action is to press the 'back' \
194
Added:
button in your browser.";
195
Added:
];
196
Added:
];
163
197
];
164
198
])
165
Removed:
166
Removed:
let repo_tree repo_path = repo_summary repo_path
167
Removed:
let repo_blob repo_path = repo_summary repo_path