/*
* The tag page, which shows a single ref under refs/tags and is where the
* branch and tag listings link. An annotated tag is a git object in its own
* right, so its page carries the tagger, the date and the message, while a
* lightweight tag is only a name for a commit and gets a shorter table. Both
* forms link the object the tag points at and, when the repository enables
* snapshots, offer the tree at that tag for download as an archive.
*/
#define USE_THE_REPOSITORY_VARIABLE
#include "cgit.h"
#include "html.h"
#include "parsing.h"
#include "ui-shared.h"
#include "ui-tag.h"
static void print_name_row(const char *revname, const struct object_id *oid)
{
html("
| tag name | ");
html_txt(revname);
if (oid)
htmlf(" (%s)", oid_to_hex(oid));
html(" |
\n");
}
static void print_date_row(struct taginfo *info)
{
html("| tag date | ");
cgit_print_date(info->tagger_date, info->tagger_tz);
html(" |
\n");
}
static void print_tagger_row(struct taginfo *info)
{
html("| tagged by | ");
cgit_print_person(info->tagger, info->tagger_email, "tag", 1);
html(" |
\n");
}
static void print_object_row(struct object *obj)
{
html("| tagged object | ");
cgit_object_link(obj, 1);
html(" |
\n");
}
/*
* A snapshot is cut from a commit, so a tag of anything else offers none, as in
* the refs listing.
*/
static void print_download_row(const char *revname, const struct object *obj)
{
if (!ctx.repo->snapshots || obj->type != OBJ_COMMIT)
return;
html("| download | ");
cgit_print_snapshot_links(ctx.repo, revname, " ");
html(" |
\n");
}
/*
* Cutting the subject out terminates the message in place, which is safe only
* because the caller frees it straight after. The blank lines between the
* subject and the body are skipped, as the commit page skips them.
*/
static void print_message(char *msg)
{
char *newline, *body;
if (!msg)
return;
html("");
newline = strchr(msg, '\n');
if (newline)
*newline = '\0';
html_txt(msg);
html("
");
if (newline) {
body = newline + 1;
while (*body == '\n')
body++;
html("");
html_txt(body);
html("
");
}
}
static void print_annotated_tag(const char *revname, struct tag *tag)
{
struct taginfo *info = cgit_parse_tag(tag);
if (!info) {
cgit_print_error_page(500, "Internal Server Error", "Bad tag object: %s", revname);
return;
}
cgit_print_layout_start();
html("\n");
print_name_row(revname, &tag->object.oid);
if (info->tagger_date > 0)
print_date_row(info);
if (info->tagger)
print_tagger_row(info);
print_object_row(tag->tagged);
print_download_row(revname, tag->tagged);
html("
\n");
print_message(info->msg);
cgit_print_layout_end();
cgit_free_taginfo(info);
}
static void print_lightweight_tag(const char *revname, struct object *obj)
{
cgit_print_layout_start();
html("\n");
print_name_row(revname, NULL);
print_object_row(obj);
print_download_row(revname, obj);
html("
\n");
cgit_print_layout_end();
}
/*
* A tag pointing at another tag links the inner one by object id, since no ref
* names it.
*/
static int tag_by_id(const char *name, struct object_id *oid)
{
return !get_oid_hex(name, oid) &&
odb_read_object_info(the_repository->objects, oid, NULL) == OBJ_TAG;
}
void cgit_print_tag(char *revname)
{
struct strbuf fullref = STRBUF_INIT;
struct object_id oid;
struct object *obj;
if (!revname)
revname = ctx.qry.head;
strbuf_addf(&fullref, "refs/tags/%s", revname);
if (repo_get_oid(the_repository, fullref.buf, &oid) && !tag_by_id(revname, &oid)) {
cgit_print_error_page(404, "Not Found", "Bad tag reference: %s", revname);
goto cleanup;
}
obj = parse_object(the_repository, &oid);
if (!obj) {
cgit_print_error_page(500, "Internal Server Error", "Unable to read object %s",
oid_to_hex(&oid));
goto cleanup;
}
cgit_prefix_title(revname);
if (obj->type == OBJ_TAG)
print_annotated_tag(revname, (struct tag *)obj);
else
print_lightweight_tag(revname, obj);
cleanup:
strbuf_release(&fullref);
}