Author | zPlus <zplus@peers.community> 2022-08-05 19:48:53 |
Committer | zPlus <zplus@peers.community> 2022-08-05 19:48:53 |
Commit | 2a5d69c (patch) |
Tree | 3c66e67 |
Parent(s) |
-rw-r--r-- | static/css/clif.css | 56 | ||
-rw-r--r-- | templates/repository/commit.html | 172 | ||
-rw-r--r-- | templates/repository/log.html | 2 | ||
-rw-r--r-- | templates/repository/refs.html | 2 | ||
-rw-r--r-- | web.py | 60 |
index 3b51603..9380f26 | |||
old size: 6K - new size: 7K | |||
@@ -320,8 +320,62 @@ div.threads { | |||
320 | 320 | .thread .info .tag { | |
321 | 321 | margin-bottom: 1rem; | |
322 | 322 | } | |
323 | - | ||
323 | + | ||
324 | + | /* Commit page */ | |
325 | + | div.commit { | |
326 | + | } | |
327 | + | ||
328 | + | div.commit .message { | |
329 | + | margin-top: 2rem; | |
330 | + | white-space: pre-wrap; | |
331 | + | } | |
332 | + | ||
333 | + | div.commit .diff { | |
334 | + | border: 1px solid #d4d4d4; | |
335 | + | border-collapse: collapse; | |
336 | + | border-spacing: 0; | |
337 | + | font-family: monospace; | |
338 | + | font-size: 1rem; | |
339 | + | margin-top: 2rem; | |
340 | + | width: 100%; | |
341 | + | } | |
342 | + | ||
343 | + | div.commit .diff thead { | |
344 | + | background-color: #f7f7f7; | |
345 | + | border: 1px solid #d4d4d4; | |
346 | + | } | |
347 | + | ||
348 | + | div.commit .diff td { | |
349 | + | padding: .1rem .5rem; | |
350 | + | } | |
351 | + | ||
352 | + | div.commit .diff .header { | |
353 | + | background-color: #f0f9ff; | |
354 | + | color: darkblue; | |
355 | + | } | |
356 | + | ||
357 | + | div.commit .diff .header td { | |
358 | + | padding: .5rem .5rem; | |
359 | + | } | |
360 | + | ||
361 | + | div.commit .diff .lineno { | |
362 | + | color: gray; | |
363 | + | text-align: center; | |
364 | + | width: 0; | |
365 | + | } | |
324 | 366 | ||
367 | + | div.commit .diff .origin { | |
368 | + | width: 0; | |
369 | + | } | |
370 | + | ||
371 | + | div.commit .diff .deletion { | |
372 | + | background-color: #ffeef0; | |
373 | + | } | |
374 | + | ||
375 | + | div.commit .diff .insertion { | |
376 | + | background-color: #e6ffed; | |
377 | + | } | |
378 | + | ||
325 | 379 | /* Alternate background color used when displaying table data */ | |
326 | 380 | .striped > *:nth-child(even) { | |
327 | 381 | background-color: #f8f8f8; |
index 0000000..39f2ca7 | |||
old size: 0B - new size: 7K | |||
new file mode: -rw-r--r-- |
@@ -0,0 +1,172 @@ | |||
1 | + | {% extends "repository/repository.html" %} | |
2 | + | ||
3 | + | {% block page_title %}Commit: {{ commit.id }}{% endblock %} | |
4 | + | ||
5 | + | {% block content %} | |
6 | + | ||
7 | + | <div class="commit"> | |
8 | + | <table> | |
9 | + | <tbody> | |
10 | + | <tr> | |
11 | + | <td> | |
12 | + | <b>Author</b> | |
13 | + | </td> | |
14 | + | <td> | |
15 | + | {{ commit.author }} | |
16 | + | {{ commit_time(commit.author.time, commit.author.offset) }} | |
17 | + | </td> | |
18 | + | </tr> | |
19 | + | <tr> | |
20 | + | <td> | |
21 | + | <b>Committer</b> | |
22 | + | </td> | |
23 | + | <td> | |
24 | + | {{ commit.committer }} | |
25 | + | {{ commit_time(commit.committer.time, commit.committer.offset) }} | |
26 | + | </td> | |
27 | + | </tr> | |
28 | + | <tr> | |
29 | + | <td> | |
30 | + | <b>Commit ID</b> | |
31 | + | </td> | |
32 | + | <td> | |
33 | + | {{ commit.id }} | |
34 | + | </td> | |
35 | + | </tr> | |
36 | + | <tr> | |
37 | + | <td> | |
38 | + | <b>Tree</b> | |
39 | + | </td> | |
40 | + | <td> | |
41 | + | <a href="{{ url('tree', repository=repository[:-4], revision=commit.tree.id) }}">{{ commit.tree.id }}</a> | |
42 | + | </td> | |
43 | + | </tr> | |
44 | + | <tr> | |
45 | + | <td> | |
46 | + | <b>Parent(s)</b> | |
47 | + | </td> | |
48 | + | <td> | |
49 | + | {% for parent in commit.parents %} | |
50 | + | <a href="{{ url('commit', repository=repository[:-4], commit_id=parent.id) }}">{{ parent.short_id }}</a> | |
51 | + | {% endfor %} | |
52 | + | </td> | |
53 | + | </tr> | |
54 | + | <tr> | |
55 | + | <td> | |
56 | + | <b>±</b> | |
57 | + | </td> | |
58 | + | <td> | |
59 | + | {{ diff.stats.files_changed }} files changed, | |
60 | + | {{ diff.stats.insertions }} insertions, | |
61 | + | {{ diff.stats.deletions }} deletions | |
62 | + | </td> | |
63 | + | </tr> | |
64 | + | </tbody> | |
65 | + | </table> | |
66 | + | ||
67 | + | <div class="message">{{ commit.message }}</div> | |
68 | + | ||
69 | + | {% for patch in diff %} | |
70 | + | ||
71 | + | <table class="diff"> | |
72 | + | ||
73 | + | {# The following status values are defined in the git_delta_t enum | |
74 | + | # in libgit2. See https://github.com/libgit2/libgit2/blob/main/include/git2/diff.h | |
75 | + | #} | |
76 | + | ||
77 | + | {# 1=ADDED (does not exist in old version) #} | |
78 | + | {# 2=DELETED (does not exist in new version) #} | |
79 | + | {# 3=MODIFIED (content changed between old and new versions) #} | |
80 | + | {# 4=RENAMED #} | |
81 | + | {# 5=COPIED #} | |
82 | + | <thead> | |
83 | + | <tr> | |
84 | + | <td colspan="4"> | |
85 | + | -{{ patch.line_stats[2] }}/+{{ patch.line_stats[1] }} | |
86 | + | ||
87 | + | {% if patch.delta.status == 1 %} | |
88 | + | <b title="Added">A</b> {{ patch.delta.new_file.path }} | |
89 | + | {% endif %} | |
90 | + | ||
91 | + | {% if patch.delta.status == 2 %} | |
92 | + | <b title="Deleted">D</b> {{ patch.delta.old_file.path }} | |
93 | + | {% endif %} | |
94 | + | ||
95 | + | {% if patch.delta.status == 3 %} | |
96 | + | <b title="Modified">M</b> {{ patch.delta.new_file.path }} | |
97 | + | {% endif %} | |
98 | + | ||
99 | + | {% if patch.delta.status == 4 %} | |
100 | + | <b title="Renamed">R</b> {{ patch.delta.old_file.path }} -> {{ patch.delta.new_file.path }} | |
101 | + | {% endif %} | |
102 | + | ||
103 | + | {% if patch.delta.status == 5 %} | |
104 | + | <b title="Copied">C</b> {{ patch.delta.old_file.path }} {{ patch.delta.new_file.path }} | |
105 | + | {% endif %} | |
106 | + | </td> | |
107 | + | </tr> | |
108 | + | <tr> | |
109 | + | <td colspan="4"> | |
110 | + | old size: {{ patch.delta.old_file.size|human_size(B=true) }} | |
111 | + | - | |
112 | + | new size: {{ patch.delta.new_file.size|human_size(B=true) }} | |
113 | + | </td> | |
114 | + | </tr> | |
115 | + | <tr> | |
116 | + | <td colspan="4"> | |
117 | + | {% if patch.delta.status == 1 %} | |
118 | + | new file mode: {{ patch.delta.new_file.mode|filemode }} | |
119 | + | {% elif patch.delta.status == 2 %} | |
120 | + | deleted file mode: {{ patch.delta.old_file.mode|filemode }} | |
121 | + | {% elif patch.delta.old_file.mode != patch.delta.new_file.mode %} | |
122 | + | old mode: {{ patch.delta.old_file.mode|filemode }} | |
123 | + | <br /> | |
124 | + | new mode: {{ patch.delta.new_file.mode|filemode }} | |
125 | + | {% endif %} | |
126 | + | </td> | |
127 | + | </tr> | |
128 | + | </thead> | |
129 | + | <tbody> | |
130 | + | {% if patch.delta.is_binary %} | |
131 | + | <tr> | |
132 | + | <td colspan="4"> | |
133 | + | <i>Binary file</i> | |
134 | + | </td> | |
135 | + | </tr> | |
136 | + | {% endif %} | |
137 | + | ||
138 | + | {% for hunk in patch.hunks if not patch.delta.is_binary %} | |
139 | + | ||
140 | + | <tr class="header"> | |
141 | + | <td></td> | |
142 | + | <td></td> | |
143 | + | <td></td> | |
144 | + | <td>{{ hunk.header }}</td> | |
145 | + | </tr> | |
146 | + | ||
147 | + | {% for line in hunk.lines %} | |
148 | + | <tr class="{{ 'insertion' if line.old_lineno < 0 }} {{ 'deletion' if line.new_lineno < 0 }}"> | |
149 | + | <td class="lineno"> | |
150 | + | {% if line.old_lineno >= 0 %} | |
151 | + | {{ line.old_lineno }} | |
152 | + | {% endif %} | |
153 | + | </td> | |
154 | + | <td class="lineno"> | |
155 | + | {% if line.new_lineno >= 0 %} | |
156 | + | {{ line.new_lineno }} | |
157 | + | {% endif %} | |
158 | + | </td> | |
159 | + | <td class="origin">{{ line.origin }}</td> | |
160 | + | <td class="content">{{ line.content }}</td> | |
161 | + | </tr> | |
162 | + | {% endfor %} | |
163 | + | ||
164 | + | {% endfor %} | |
165 | + | </tbody> | |
166 | + | ||
167 | + | </table> | |
168 | + | ||
169 | + | {% endfor %} | |
170 | + | </div> | |
171 | + | ||
172 | + | {% endblock %} |
index 2ddb8f8..2ec71a7 | |||
old size: 4K - new size: 4K | |||
@@ -56,7 +56,7 @@ | |||
56 | 56 | {% endif %} | |
57 | 57 | </td> | |
58 | 58 | <td class="short_id" title="{{ commit.id }}"> | |
59 | - | {{ commit.short_id }} | |
59 | + | <a href="{{ url('commit', repository=repository[:-4], commit_id=commit.id) }}">{{ commit.short_id }}</a> | |
60 | 60 | </td> | |
61 | 61 | <td class="message"> | |
62 | 62 | {% if commit.message|length <= 100 %} |
index e2b4594..ddec0d7 | |||
old size: 3K - new size: 3K | |||
@@ -25,7 +25,7 @@ | |||
25 | 25 | {% endif %} | |
26 | 26 | </td> | |
27 | 27 | <td> | |
28 | - | [{{ head.commit.short_id }}] | |
28 | + | <a href="{{ url('commit', repository=repository[:-4], commit_id=head.commit.id) }}">{{ head.commit.short_id }}</a> | |
29 | 29 | {{ head.commit.message[:80] }}... | |
30 | 30 | </td> | |
31 | 31 | <td> |
index 772b5eb..d007ed6 | |||
old size: 25K - new size: 25K | |||
@@ -345,18 +345,12 @@ def tree(repository, revision, tree_path=None): | |||
345 | 345 | ||
346 | 346 | if repo.is_empty: | |
347 | 347 | return template('repository/tree.html', | |
348 | - | repository=repository, revision=revision) | |
349 | - | ||
350 | - | git_object = None | |
348 | + | repository=repository, revision=revision, offset=0) | |
351 | 349 | ||
352 | 350 | try: | |
353 | 351 | git_object = repo.revparse_single(revision) | |
354 | 352 | except: | |
355 | - | pass | |
356 | - | ||
357 | - | if not git_object: | |
358 | - | return template('repository/tree.html', | |
359 | - | repository=repository, revision=revision) | |
353 | + | bottle.abort(404) | |
360 | 354 | ||
361 | 355 | # List all the references. | |
362 | 356 | # This is used for allowing the user to switch revision with a selector. | |
@@ -443,6 +437,10 @@ def log(repository, revision): | |||
443 | 437 | repository += '.git' | |
444 | 438 | repository_path = os.path.join(GITOLITE_REPOSITORIES_ROOT, repository) | |
445 | 439 | ||
440 | + | # Read commits | |
441 | + | try: commits_offset = int(request.query.get('offset', 0)) | |
442 | + | except: commits_offset = 0 | |
443 | + | ||
446 | 444 | if not os.path.isdir(repository_path): | |
447 | 445 | bottle.abort(404, 'No repository at this path.') | |
448 | 446 | ||
@@ -450,18 +448,12 @@ def log(repository, revision): | |||
450 | 448 | ||
451 | 449 | if repo.is_empty: | |
452 | 450 | return template('repository/log.html', | |
453 | - | repository=repository, revision=revision) | |
454 | - | ||
455 | - | git_object = None | |
451 | + | repository=repository, revision=revision, offset=commits_offset) | |
456 | 452 | ||
457 | 453 | try: | |
458 | 454 | git_object = repo.revparse_single(revision) | |
459 | 455 | except: | |
460 | - | pass | |
461 | - | ||
462 | - | if not git_object: | |
463 | - | return template('repository/log.html', | |
464 | - | repository=repository, revision=revision) | |
456 | + | bottle.abort(404) | |
465 | 457 | ||
466 | 458 | # List all the references. | |
467 | 459 | # This is used for allowing the user to switch revision with a selector. | |
@@ -487,10 +479,6 @@ def log(repository, revision): | |||
487 | 479 | ||
488 | 480 | # At this point git_object should be a valid pygit2.GIT_OBJ_COMMIT | |
489 | 481 | ||
490 | - | # Read commits | |
491 | - | try: commits_offset = int(request.query.get('offset', 0)) | |
492 | - | except: commits_offset = 0 | |
493 | - | ||
494 | 482 | commits = [] | |
495 | 483 | diff = {} | |
496 | 484 | commit_ith = 0 | |
@@ -533,6 +521,38 @@ def log_change(repository): | |||
533 | 521 | repository=repository, | |
534 | 522 | revision=revision)) | |
535 | 523 | ||
524 | + | @bottle.get('/<repository:path>.git/commit/<commit_id>', name='commit') | |
525 | + | def commit(repository, commit_id): | |
526 | + | """ | |
527 | + | Show a commit. | |
528 | + | """ | |
529 | + | ||
530 | + | repository += '.git' | |
531 | + | repository_path = os.path.join(GITOLITE_REPOSITORIES_ROOT, repository) | |
532 | + | ||
533 | + | if not os.path.isdir(repository_path): | |
534 | + | bottle.abort(404, 'No repository at this path.') | |
535 | + | ||
536 | + | repo = pygit2.Repository(repository_path) | |
537 | + | ||
538 | + | try: | |
539 | + | commit = repo.get(commit_id) | |
540 | + | assert commit.type == pygit2.GIT_OBJ_COMMIT | |
541 | + | except: | |
542 | + | bottle.abort(404, 'Not a valid commit.') | |
543 | + | ||
544 | + | # Find diff wih parent | |
545 | + | if len(commit.parents) > 0: | |
546 | + | diff = commit.parents[0].tree.diff_to_tree(commit.tree) | |
547 | + | else: | |
548 | + | diff = commit.tree.diff_to_tree(swap=True) | |
549 | + | ||
550 | + | diff.find_similar() | |
551 | + | ||
552 | + | return template( | |
553 | + | 'repository/commit.html', | |
554 | + | repository=repository, commit=commit, diff=diff) | |
555 | + | ||
536 | 556 | @bottle.get('/<repository:path>.git/raw/<revision>/<tree_path:path>', name='raw') | |
537 | 557 | def raw(repository, revision, tree_path): | |
538 | 558 | """ |