neocgit

a more 'modern' version of cgit
Log | Files | Refs | Submodules | README | LICENSE | git clone https://git.ne02ptzero.me/git/neocgit

commit fc70487b9b61462f6ec6d88f849d1a36219ca0e7
parent f47c28be72a1deaf5fe3e5eca8f315a539fcc191
Author: Ne02ptzero <louis@ne02ptzero.me>
Date:   Mon,  2 Jul 2018 17:16:55 +0200

FIX: Tree is now fully working

Signed-off-by: Ne02ptzero <louis@ne02ptzero.me>

Diffstat:
Mcgit.css | 50+++++++++++++++++++++++++++++++++++++++++++++++++-
Mhtml.h | 1+
Mui-log.c | 19+++++++++----------
Mui-shared.c | 89++++++++++++++++++++++++++++++++-----------------------------------------------
Mui-summary.c | 46+++++++++++++++++++++++++++-------------------
Mui-tree.c | 316++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
6 files changed, 306 insertions(+), 215 deletions(-)

diff --git a/cgit.css b/cgit.css @@ -288,6 +288,16 @@ div#cgit table { padding-left: 15px; } +.summary-branches ul .repo-name { + color: #999; +} + +.summary-branches ul .repo-name a { + color: black !important; + margin-left: 10px; + margin-right: 10px; +} + .summary-branches ul .history { text-align: right; } @@ -359,8 +369,9 @@ div#cgit table { width: 100%; } -.last-commit ul .commit-info ul .title { +.last-commit ul .commit-info ul .title a { font-weight: bold; + color: #333 !important; } .last-commit .hash { @@ -392,15 +403,31 @@ div#cgit table { border-top: 1px solid #f0f0f0; } +.repo-tree table .tree-entry:hover { + cursor: pointer; + background: #fafafa; + border-top: 1px solid #7575ea4d; + border-bottom: 1px solid #7575ea4d; +} + .repo-tree table tr td { padding: 10px 16px; color: #707070; } .repo-tree table tr td a { + color: #707070 !important; +} + +.repo-tree table tr .file-path span { color: black !important; } +.repo-tree table tr .file-path i { + color: #2e2e2e; + margin-right: 7px; +} + .repo-tree table tr th { background-color: #fafafa; font-weight: 400; @@ -417,6 +444,27 @@ div#cgit table { text-align: right; } +.repo-tree .file-path { + max-width: 320px; +} + +.repo-readme { + margin: 10px 15px; + border: 1px solid #e5e5e5; + border-radius: 4px; +} + +.repo-readme .readme-header { + background: #fafafa; + border-bottom: 1px solid #e5e5e5; + padding: 10px 16px; + border-radius: 4px 4px 0 0; +} + +.repo-readme .content { + padding: 32px; +} + div#cgit div.path { margin: 0px; padding: 5px 2em 2px 2em; diff --git a/html.h b/html.h @@ -10,6 +10,7 @@ extern void html_raw(const char *txt, size_t size); #define _html(txt) html(txt "\n"); +#define _htmlf(txt, ...) htmlf(txt "\n", __VA_ARGS__); extern void html(const char *txt); __attribute__((format (printf,1,2))) diff --git a/ui-log.c b/ui-log.c @@ -393,11 +393,13 @@ struct commit *cgit_get_last_commit_from_path(const char *path) load_ref_decorations(NULL, DECORATE_FULL_REFS); rev.max_count = 1; - prepare_revision_walk(&rev); + if (prepare_revision_walk(&rev) != 0) + die("Walk error"); + commit = get_revision(&rev); object_array_clear(&rev.pending); - reset_revision_walk(); + clear_object_flags(ALL_REV_FLAGS); if (must_free_tip) free((char *)tip); @@ -436,15 +438,17 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern int i, columns = commit_graph ? 4 : 3; int must_free_tip = 0; + if (pager) { + cgit_print_layout_start(); + html("<table class='list nowrap'>"); + } /* rev_argv.argv[0] will be ignored by setup_revisions */ argv_array_push(&rev_argv, "log_rev_setup"); - if (!tip) tip = ctx.qry.head; tip = disambiguate_ref(tip, &must_free_tip); - printf("Tip: %s, Path: %s\n", tip, path); argv_array_push(&rev_argv, tip); if (grep && pattern && *pattern) { @@ -499,7 +503,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern argv_array_push(&rev_argv, path); init_revisions(&rev, NULL); - rev.abbrev = DEFAULT_ABBREV; + rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.verbose_header = 1; rev.show_root_diff = 0; @@ -519,11 +523,6 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern prepare_revision_walk(&rev); reset_revision_walk(); - if (pager) { - cgit_print_layout_start(); - html("<table class='list nowrap'>"); - } - html("<tr class='nohover'>"); if (commit_graph) html("<th></th>"); diff --git a/ui-shared.c b/ui-shared.c @@ -777,6 +777,7 @@ void cgit_print_docstart(void) cgit_add_clone_urls(print_rel_vcs_link); if (ctx.cfg.head_include) html_include(ctx.cfg.head_include); + html("<link rel='stylesheet' href='https://use.fontawesome.com/releases/v5.1.0/css/all.css' integrity='sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt' crossorigin='anonymous'>"); html("</head>\n"); html("<body>\n"); if (ctx.cfg.header) @@ -1043,6 +1044,9 @@ void cgit_print_pageheader(void) } _html("</ul>"); } _html("</div>"); + if (strcmp("commit", ctx.qry.page) == 0) + goto end; + _html("<div class='summary-branches'>") { _html("<ul>") { _html("<li>") { @@ -1055,11 +1059,28 @@ void cgit_print_pageheader(void) } _html("</li>"); _html("<li class='repo-name'>") { - html_txt(ctx.repo->name); + htmlf("<a href='/%s?h=%s'>%s</a>", ctx.repo->name, ctx.qry.head, ctx.repo->name); + if (ctx.qry.path) + { + int z = 0; + + for (int i = 0; ctx.qry.path[i] != '\0'; i++) + { + if (ctx.qry.path[i] != '/' || i == 0) + continue; + + ctx.qry.path[i] = 0; + htmlf("/<a href='/%s/tree/%s?h=%s'>%s</a>", ctx.repo->name, ctx.qry.path, ctx.qry.head, ctx.qry.path + z); + ctx.qry.path[i] = '/'; + z = ++i; + } + + htmlf("/<a href='/%s/tree/%s?h=%s'>%s</a>", ctx.repo->name, ctx.qry.path, ctx.qry.head, ctx.qry.path + z); + } } _html("</li>"); _html("<li class='history'>") { - htmlf("<a href='/%s/log'>History</a>", ctx.repo->name); + htmlf("<a href='/%s/log/%s?h=%s'>History</a>", ctx.repo->name, ctx.qry.path, ctx.qry.head); } _html("</li>"); } _html("</ul>"); } _html("</div>"); @@ -1070,8 +1091,13 @@ void cgit_print_pageheader(void) struct commitinfo *info; struct object_id oid; - get_oid(ctx.qry.head, &oid); - commit = lookup_commit_reference(&oid); + if (ctx.qry.path) + commit = cgit_get_last_commit_from_path(ctx.qry.path); + else + { + get_oid(ctx.qry.head, &oid); + commit = lookup_commit_reference(&oid); + } info = cgit_parse_commit(commit); _html("<ul>") { @@ -1083,7 +1109,8 @@ void cgit_print_pageheader(void) } _html("</li>"); _html("<li class='commit-info'><ul>") { - htmlf("<li class='title'>%s</li>", info->subject); + htmlf("<li class='title'><a href='/%s/commit/?id=%s'>%s</a></li>", + ctx.repo->name, oid_to_hex(&commit->object.oid), info->subject); htmlf("<li class='author'>%s authored ", info->author); cgit_print_age(info->author_date, info->author_tz, -1); html(" ago</li>"); @@ -1091,61 +1118,17 @@ void cgit_print_pageheader(void) _html("<li class='hash'>") { html("<input type='text' value='"); - html_txt(oid_to_hex(&oid)); + html_txt(oid_to_hex(&commit->object.oid)); html("' readonly />"); } _html("</li>"); } _html("</ul>"); } _html("</div>"); } _html("</div>"); + } - /* if (ctx.repo->readme.nr)*/ - /*reporevlink("about", "about", NULL,*/ - /*hc("about"), ctx.qry.head, NULL,*/ - /*NULL);*/ - /*cgit_summary_link("summary", NULL, hc("summary"),*/ - /*ctx.qry.head);*/ - /*cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,*/ - /*ctx.qry.sha1, NULL);*/ - /*cgit_log_link("log", NULL, hc("log"), ctx.qry.head,*/ - /*NULL, ctx.qry.vpath, 0, NULL, NULL,*/ - /*ctx.qry.showmsg, ctx.qry.follow);*/ - /*if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))*/ - /*cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,*/ - /*ctx.qry.sha1, ctx.qry.vpath);*/ - /*else*/ - /*cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,*/ - /*ctx.qry.sha1, ctx.qry.vpath);*/ - /*cgit_commit_link("commit", NULL, hc("commit"),*/ - /*ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath);*/ - /*cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,*/ - /*ctx.qry.sha1, ctx.qry.sha2, ctx.qry.vpath);*/ - /*if (ctx.repo->max_stats)*/ - /*cgit_stats_link("stats", NULL, hc("stats"),*/ - /*ctx.qry.head, ctx.qry.vpath);*/ - /*if (ctx.repo->homepage) {*/ - /*html("<a href='");*/ - /*html_attr(ctx.repo->homepage);*/ - /*html("'>homepage</a>");*/ - /*}*/ - /*html("</td><td class='form'>");*/ - } - /*html("</td></tr></table>\n");*/ - if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) { - html("<div class='path'>"); - html("path: "); - cgit_print_path_crumbs(ctx.qry.vpath); - if (ctx.cfg.enable_follow_links && !strcmp(ctx.qry.page, "log")) { - html(" ("); - ctx.qry.follow = !ctx.qry.follow; - cgit_self_link(ctx.qry.follow ? "follow" : "unfollow", - NULL, NULL); - ctx.qry.follow = !ctx.qry.follow; - html(")"); - } - html("</div>"); - } - html("<div class='content'>"); +end: + html("<div class='content'>"); } void cgit_print_filemode(unsigned short mode) diff --git a/ui-summary.c b/ui-summary.c @@ -52,7 +52,7 @@ void cgit_print_summary(void) cgit_print_layout_start(); cgit_print_tree(ctx.qry.sha1, ctx.qry.path, false); - /*cgit_print_repo_readme(ctx.qry.path, false);*/ + cgit_print_repo_readme(ctx.qry.path, false); cgit_print_layout_end(); } @@ -114,25 +114,33 @@ void cgit_print_repo_readme(char *path, bool layout) filename = ctx.repo->readme.items[0].string; ref = ctx.repo->readme.items[0].util; - if (path) { - free_filename = 1; - filename = append_readme_path(filename, ref, path); - if (!filename) - goto done; - } + _html("<div class='repo-readme'>") { + _html("<div class='readme-header'>") { + htmlf("%s", filename); + } _html("</div>"); + + _html("<div class='content'>") { + + if (path) { + free_filename = 1; + filename = append_readme_path(filename, ref, path); + if (!filename) + goto done; + } + + /* Print the calculated readme, either from the git repo or from the + * filesystem, while applying the about-filter. + */ + cgit_open_filter(ctx.repo->about_filter, filename); + if (ref) + cgit_print_file(filename, ref, 1); + else + html_include(filename); + cgit_close_filter(ctx.repo->about_filter); + } _html("</div>"); + + } _html("</div>"); - /* Print the calculated readme, either from the git repo or from the - * filesystem, while applying the about-filter. - */ - html("<div id='summary'>"); - cgit_open_filter(ctx.repo->about_filter, filename); - if (ref) - cgit_print_file(filename, ref, 1); - else - html_include(filename); - cgit_close_filter(ctx.repo->about_filter); - - html("</div>"); if (free_filename) free(filename); diff --git a/ui-tree.c b/ui-tree.c @@ -29,6 +29,87 @@ struct walk_tree_context { struct tree_link *head; }; +static bool format_match(const char **array, size_t size, const char *filename) +{ + int format_cur; + + for (format_cur = strlen(filename); format_cur > 0 && filename[format_cur] != '.'; format_cur--) + ; + + if (format_cur == 0) + return false; + + for (size_t i = 0; i < size; i++) + { + if (strcmp(array[i], filename + format_cur) == 0) + return true; + } + + return false; +} + +#define COUNT_OF(arr) (sizeof(arr) / sizeof(arr[0])) +static bool file_is_code(const char *filename) +{ + const char *formats[] = { + ".c", ".cpp", ".h", ".hpp", ".cxx", ".cc", + ".js", ".html", ".css", ".py", ".php" // XXX + }; + + return format_match(formats, COUNT_OF(formats), filename); +} + +static bool file_is_audio(const char *filename) +{ + const char *formats[] = { + ".3gp", ".aa", ".aac", ".aax", ".act", ".aiff", ".amr", ".ape", ".au", + ".awb", ".dct", ".dss", ".dvf", ".flac", ".gsm", ".m4a", ".m4b", ".m4p", + ".mmf", ".mp3", ".mpc", ".msv", ".nsf", ".ogg", ".oga", ".mogg", ".opus", + ".ra", ".rm", ".sln", ".tta", ".vox", ".wav", ".wma", ".wv", ".webm" + }; + + return format_match(formats, COUNT_OF(formats), filename); +} + +static bool file_is_pdf(const char *filename) +{ + const char *formats[] = { + ".pdf" + }; + + return format_match(formats, COUNT_OF(formats), filename); +} + +static bool file_is_video(const char *filename) +{ + const char *formats[] = { + ".yuv", ".wmv", ".vob", ".svi", ".roq", ".rmvb", ".mpg", ".mpeg", ".m2v", + ".mp2", ".mpe", ".mpv", ".mp4", ".m4p", ".m4v", ".mov", ".qt", ".mng", + ".mkv", ".m4v", ".gifv", ".gif", ".flv", ".drc", ".avi", ".asf", ".amv", + ".3gp", ".3g2" + }; + + return format_match(formats, COUNT_OF(formats), filename); +} + +static bool file_is_archive(const char *filename) +{ + const char *formats[] = { + ".tar", ".gz", ".zip", ".ar", ".a" + }; + + return format_match(formats, COUNT_OF(formats), filename); +} + +static bool file_is_image(const char *filename) +{ + const char *formats[] = { + ".png", ".jpg", ".jpeg", ".webp", ".svg", ".ai", ".eps" + }; + + return format_match(formats, COUNT_OF(formats), filename); +} + static void print_text_buffer(const char *name, char *buf, unsigned long size) { unsigned long lineno, idx; @@ -210,9 +291,33 @@ static void write_tree_link(const struct object_id *oid, char *name, strbuf_setlen(fullpath, initial_length); } +static void print_file_icon(const char *filename, int mode) +{ + if (S_ISLNK(mode)) + html("<i class='fa fa-file-export'></i>"); + else if (S_ISDIR(mode)) + html("<i class='fa fa-folder'></i>"); + else + { + if (file_is_code(filename)) + html("<i class='far fa-file-code'></i>"); + else if (file_is_audio(filename)) + html("<i class='far fa-file-audio'></i>"); + else if (file_is_video(filename)) + html("<i class='far fa-file-video'></i>"); + else if (file_is_pdf(filename)) + html("<i class='far fa-file-pdf'></i>"); + else if (file_is_archive(filename)) + html("<i class='far fa-file-archive'></i>"); + else if (file_is_image(filename)) + html("<i class='far fa-file-image'></i>"); + else + html("<i class='far fa-file-alt'></i>"); + } +} + static void print_tree_item(struct tree_link *ptr, struct walk_tree_context *tree_ctx) { - struct strbuf class = STRBUF_INIT; struct strbuf fullpath = STRBUF_INIT; struct commitinfo *info = NULL; struct commit *commit = NULL; @@ -222,21 +327,19 @@ static void print_tree_item(struct tree_link *ptr, struct walk_tree_context *tre strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", ctx.qry.path ? "/" : "", name); - commit = cgit_get_last_commit_from_path(name); + commit = cgit_get_last_commit_from_path(fullpath.buf); if (commit) info = cgit_parse_commit(commit); - _html("<tr>") { - _html("<td class='table-left'>") { - cgit_tree_link(name, NULL, class.buf, ctx.qry.head, tree_ctx->curr_rev, - fullpath.buf); + _htmlf("<tr onclick=\"location.href='/%s/tree/%s?h=%s'\" class='tree-entry'>", ctx.repo->name, fullpath.buf, ctx.qry.head) { + _html("<td class='table-left file-path'>") { + print_file_icon(name, ptr->mode); + htmlf("<span>%s</span>", name); } _html("</td>"); _html("<td class='table-left'>") { - if (info) - { - htmlf("%s", info->subject); - } + if (info) + htmlf("<a href='/%s/commit/?id=%s'>%s</a>", ctx.repo->name, oid_to_hex(&commit->object.oid), info->subject); else html("[ERROR]"); } _html("</td>"); @@ -257,144 +360,63 @@ static void print_tree_item(struct tree_link *ptr, struct walk_tree_context *tre free_commit_buffer(commit); free(info); } + + free(name); } static int ls_item(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *cbdata) { - struct tree_link *obj; - struct walk_tree_context *walk_tree_ctx = cbdata; - - obj = calloc(1, sizeof(*obj)); - obj->oid = oid; - obj->base = base; - obj->pathname = strdup(pathname); - obj->mode = mode; - obj->stage = stage; + struct tree_link *obj; + struct walk_tree_context *walk_tree_ctx = cbdata; + + obj = calloc(1, sizeof(*obj)); + obj->oid = oid; + obj->base = base; + obj->pathname = strdup(pathname); + obj->mode = mode; + obj->stage = stage; + + if (walk_tree_ctx->head == NULL) + walk_tree_ctx->head = obj; + else + { + struct tree_link *tmp; - if (walk_tree_ctx->head == NULL) - walk_tree_ctx->head = obj; - else + for (tmp = walk_tree_ctx->head; tmp->next != NULL; tmp = tmp->next) { - struct tree_link *tmp; - - for (tmp = walk_tree_ctx->head; tmp->next != NULL; tmp = tmp->next) + if (strcmp(tmp->pathname, pathname) > 0 + || (S_ISDIR(mode) && !S_ISDIR(tmp->mode))) { - if (strcmp(tmp->pathname, pathname) > 0 - || (S_ISDIR(mode) && !S_ISDIR(tmp->mode))) - { - break ; - } + break ; } + } - if (tmp->next == NULL) - { - obj->prev = tmp; - tmp->next = obj; - } - else if (tmp->prev == NULL) - { - tmp->prev = obj; - obj->next = tmp; - walk_tree_ctx->head = obj; - } - else - { - tmp->prev->next = obj; - obj->next = tmp; - obj->next->prev = obj; - } + if (tmp->next == NULL && (S_ISDIR(tmp->mode) || (!S_ISDIR(mode) && !S_ISDIR(tmp->mode)))) + { + obj->prev = tmp; + tmp->next = obj; } - /* char *name;*/ - /*struct strbuf fullpath = STRBUF_INIT;*/ - /*struct strbuf class = STRBUF_INIT;*/ - /*enum object_type type;*/ - /*unsigned long size = 0;*/ - - /*name = xstrdup(pathname);*/ - /*strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "",*/ - /*ctx.qry.path ? "/" : "", name);*/ - - /*if (!S_ISGITLINK(mode)) {*/ - /*type = oid_object_info(the_repository, oid, &size);*/ - /*if (type == OBJ_BAD) {*/ - /*htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",*/ - /*name,*/ - /*oid_to_hex(oid));*/ - /*free(name);*/ - /*return 0;*/ - /*}*/ - /*}*/ - - /*_html("<tr>") {*/ - - /*_html("<td class='table-left'>") {*/ - /*cgit_tree_link(name, NULL, class.buf, ctx.qry.head,*/ - /*walk_tree_ctx->curr_rev, fullpath.buf);*/ - /*} _html("</td>");*/ - - /*_html("<td class='table-left'>") {*/ - /*struct commit co = { 0 };*/ - - /*cgit_get_last_commit_from_path(name, &co);*/ - /*struct commitinfo *info = cgit_parse_commit(&co);*/ - /*htmlf("Msg: %s, Author: %s", info->subject, info->author);*/ - - /*} _html("</td>");*/ - - /*_html("<td class='table-right'>") {*/ - /*html("Update");*/ - /*} _html("</td>");*/ - /*} _html("</tr>");*/ - - /*html("<tr><td class='ls-mode'>");*/ - /*cgit_print_filemode(mode);*/ - /*html("</td><td>");*/ - /*if (S_ISGITLINK(mode)) {*/ - /*cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid));*/ - /*} else if (S_ISDIR(mode)) {*/ - /*write_tree_link(oid, name, walk_tree_ctx->curr_rev,*/ - /*&fullpath);*/ - /*} else {*/ - /*char *ext = strrchr(name, '.');*/ - /*strbuf_addstr(&class, "ls-blob");*/ - /*if (ext)*/ - /*strbuf_addf(&class, " %s", ext + 1);*/ - /*cgit_tree_link(name, NULL, class.buf, ctx.qry.head,*/ - /*walk_tree_ctx->curr_rev, fullpath.buf);*/ - /*}*/ - /*htmlf("</td><td class='ls-size'>%li</td>", size);*/ - - /*html("<td>");*/ - /*cgit_log_link("log", NULL, "button", ctx.qry.head,*/ - /*walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL,*/ - /*ctx.qry.showmsg, 0);*/ - /*if (ctx.repo->max_stats)*/ - /*cgit_stats_link("stats", NULL, "button", ctx.qry.head,*/ - /*fullpath.buf);*/ - /*if (!S_ISGITLINK(mode))*/ - /*cgit_plain_link("plain", NULL, "button", ctx.qry.head,*/ - /*walk_tree_ctx->curr_rev, fullpath.buf);*/ - /*if (!S_ISDIR(mode) && ctx.cfg.enable_blame)*/ - /*cgit_blame_link("blame", NULL, "button", ctx.qry.head,*/ - /*walk_tree_ctx->curr_rev, fullpath.buf);*/ - /*html("</td></tr>\n");*/ - /*free(name);*/ - /*strbuf_release(&fullpath);*/ - /*strbuf_release(&class);*/ - return 0; + else if (tmp->prev == NULL) + { + tmp->prev = obj; + obj->next = tmp; + walk_tree_ctx->head = obj; + } + else + { + tmp->prev->next = obj; + obj->next = tmp; + obj->next->prev = obj; + } + } + + return 0; } static void ls_head(void) { cgit_print_layout_start(); - html("<table summary='tree listing' class='list'>\n"); - html("<tr class='nohover'>"); - html("<th class='left'>Mode</th>"); - html("<th class='left'>Name</th>"); - html("<th class='right'>Size</th>"); - html("<th/>"); - html("</tr>\n"); } static void ls_tail(void) @@ -427,6 +449,13 @@ static void ls_tree(const struct object_id *oid, char *path, struct walk_tree_co html("<th class='table-right'>Last update</th>"); } _html("</tr>"); + if (ctx.qry.path) + { + htmlf("<tr onclick=\"location.href='/%s/tree/%s/../?h=%s'\" class='tree-entry'>" + "<td><span>..</span></td><td></td><td</td></tr>", + ctx.repo->name, ctx.qry.path, ctx.qry.head); + } + walk_tree_ctx->head = NULL; read_tree_recursive(tree, "", 0, 1, &paths, ls_item, walk_tree_ctx); free_tree_buffer(tree); @@ -518,6 +547,29 @@ void cgit_print_tree(const char *rev, char *path, bool layout) } read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); + + if (walk_tree_ctx.head != NULL) + { + html("<div class='repository-tree'><div class='repo-tree'><table>"); + _html("<tr class='table-title'>") { + html("<th class='table-left'>Name</th>"); + html("<th class='table-left'>Last commit</th>"); + html("<th class='table-right'>Last update</th>"); + } _html("</tr>"); + + + if (ctx.qry.path) + { + htmlf("<tr onclick=\"location.href='/%s/tree/%s/../?h=%s'\" class='tree-entry'>" + "<td><span>..</span></td><td></td><td</td></tr>", + ctx.repo->name, ctx.qry.path, ctx.qry.head); + } + + for (struct tree_link *tmp = walk_tree_ctx.head; tmp; tmp = tmp->next) + print_tree_item(tmp, &walk_tree_ctx); + html("</table></div></div>"); + } + if (walk_tree_ctx.state == 1) ls_tail(); else if (walk_tree_ctx.state == 2)