Bug: cgit shows empty pages, when viewing Tag-Only-Repo

John Keeping john at keeping.me.uk
Wed Jun 20 16:23:33 CEST 2018

On Fri, May 04, 2018 at 08:20:10AM +0200, Björn Lässig wrote:
>  I use repositories for holding some tags.
> Unfortunately cgit shows only
>   <red>Repository seems to be empty</red>
> I looked into the code and saw the problem, but i have not the insight
> to fix this.
> one (small) example for the Problem is:
> https://git.pengutronix.de/cgit/ukl/distrokit/refs/
> git ls-remote https://git.pengutronix.de/git/ukl/distrokit/
> shows a handful of refs.

I had a related patch hanging around which improves the behaviour when
the default branch doesn't exist, and it is easy to expand this to pick
a default ref when there are no branches.

I'm not sure if I sent this to the list in the past; certainly there are
arguments as to why this behaviour isn't desirable.

The requirement for a default ref is baked in throughout the CGit code
base and I don't think changing that is worthwhile, but providing we can
pick some ref as the default, it doesn't necessarily have to be a
branch.  So this patch allows any ref to be the default (albeit with a
strong preference for branches if any exist).

-- >8 --
Subject: [PATCH] cgit: improve default branch selection

If the named default branch does not exist (for example because none is
specified in the config and "master" is not used in the repositry), then
we currently select the first branch in whatever order Git returns them
to us.

Instead, let's use the newest branch (by commit date), since this is
likely to be the most interesting branch in the repository.  This does
make default branch selection more expensive, but people who care should
specify a default branch in their repository configuration.

Further, if a repository has no branches but does have tags, let's pick
the newest tag as the default head [1].  This is a separate pass because
we want to prefer branches if there are any.

[1] In fact, we allow any ref at this point because we want to avoid
    falsely claiming that the repository is empty when it isn't.

Signed-off-by: John Keeping <john at keeping.me.uk>
 cgit.c | 46 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 35 insertions(+), 11 deletions(-)

diff --git a/cgit.c b/cgit.c
index bd9cb3f..43b8c96 100644
--- a/cgit.c
+++ b/cgit.c
@@ -430,7 +430,6 @@ static void prepare_context(void)
 struct refmatch {
 	char *req_ref;
-	char *first_ref;
 	int match;
@@ -442,34 +441,59 @@ static int find_current_ref(const char *refname, const struct object_id *oid,
 	info = (struct refmatch *)cb_data;
 	if (!strcmp(refname, info->req_ref))
 		info->match = 1;
-	if (!info->first_ref)
-		info->first_ref = xstrdup(refname);
 	return info->match;
-static void free_refmatch_inner(struct refmatch *info)
+struct newest_ref {
+	char *ref;
+	unsigned long date;
+static int find_newest_ref(const char *refname, const struct object_id *oid,
+			   int flags, void *cb_data)
-	if (info->first_ref)
-		free(info->first_ref);
+	struct newest_ref *newest = cb_data;
+	struct object *object;
+	unsigned long date;
+	object = parse_object(oid);
+	if (object->type == OBJ_COMMIT) {
+		date = ((struct commit *)object)->date;
+		if (date > newest->date) {
+			free(newest->ref);
+			newest->ref = xstrdup(refname);
+			newest->date = date;
+		}
+	}
+	return 0;
 static char *find_default_branch(struct cgit_repo *repo)
 	struct refmatch info;
+	struct newest_ref newest = { NULL };
 	char *ref;
 	info.req_ref = repo->defbranch;
-	info.first_ref = NULL;
 	info.match = 0;
 	for_each_branch_ref(find_current_ref, &info);
-	if (info.match)
+	if (info.match) {
 		ref = info.req_ref;
-	else
-		ref = info.first_ref;
+	} else {
+		/*
+		 * If the default branch isn't found, pick the most recently
+		 * updated branch, and if there are no branches, check if
+		 * this is a tag-only repo and pick any ref we can.
+		 */
+		for_each_branch_ref(find_newest_ref, &newest);
+		if (!newest.ref)
+			for_each_ref(find_newest_ref, &newest);
+		ref = newest.ref;
+	}
 	if (ref)
 		ref = xstrdup(ref);
-	free_refmatch_inner(&info);
+	free(newest.ref);
 	return ref;

More information about the CGit mailing list