cgit — Snapshot System

Overview

cgit can generate downloadable source archives (snapshots) from any git reference. Supported formats include tar, compressed tar variants, and zip. The snapshot system validates requests against a configured format mask and delegates archive generation to the git archive API.

Source file: ui-snapshot.c, ui-snapshot.h.

Snapshot Format Table

All supported formats are defined in cgit_snapshot_formats[]:

const struct cgit_snapshot_format cgit_snapshot_formats[] = {
    { ".zip",      "application/x-zip",     write_zip_archive,     0x01 },
    { ".tar.gz",   "application/x-gzip",    write_tar_gzip_archive, 0x02 },
    { ".tar.bz2",  "application/x-bzip2",   write_tar_bzip2_archive, 0x04 },
    { ".tar",      "application/x-tar",     write_tar_archive,     0x08 },
    { ".tar.xz",   "application/x-xz",      write_tar_xz_archive,  0x10 },
    { ".tar.zst",  "application/x-zstd",    write_tar_zstd_archive, 0x20 },
    { ".tar.lz",   "application/x-lzip",    write_tar_lzip_archive, 0x40 },
    { NULL }
};

Format Structure

struct cgit_snapshot_format {
    const char *suffix;        /* file extension */
    const char *mimetype;      /* HTTP Content-Type */
    write_archive_fn_t fn;     /* archive writer function */
    int bit;                   /* bitmask flag */
};

Format Bitmask

Each format has a power-of-two bit value. The snapshots configuration directive sets a bitmask by OR-ing the bits of enabled formats:

Suffix Bit Hex
.zip 0x01 1
.tar.gz 0x02 2
.tar.bz2 0x04 4
.tar 0x08 8
.tar.xz 0x10 16
.tar.zst 0x20 32
.tar.lz 0x40 64
all 0x7F 127

Parsing Snapshot Configuration

cgit_parse_snapshots_mask() in shared.c converts the configuration string to a bitmask:

int cgit_parse_snapshots_mask(const char *str)
{
    int mask = 0;
    /* for each word in str */
    /* compare against cgit_snapshot_formats[].suffix */
    /* if match, mask |= format->bit */
    /* "all" enables all formats */
    return mask;
}

Snapshot Request Processing

Entry Point: cgit_print_snapshot()

void cgit_print_snapshot(const char *head, const char *hex,
                         const char *prefix, const char *filename,
                         int snapshots)

Parameters:

  • head — Branch/tag reference
  • hex — Commit SHA
  • prefix — Archive prefix (directory name within archive)
  • filename — Requested filename (e.g., myrepo-v1.0.tar.gz)
  • snapshots — Enabled format bitmask

Reference Resolution: get_ref_from_filename()

Decomposes the requested filename into a reference and format:

static const struct cgit_snapshot_format *get_ref_from_filename(
    const char *filename, char **ref)
{
    /* for each format suffix */
    /*   if filename ends with suffix */
    /*     extract the part before the suffix as the ref */
    /*     return the matching format */
    /* strip repo prefix if present */
}

Example decomposition:

  • myrepo-v1.0.tar.gz → ref=v1.0, format=.tar.gz
  • myrepo-main.zip → ref=main, format=.zip
  • myrepo-abc1234.tar.xz → ref=abc1234, format=.tar.xz

The prefix myrepo- is the snapshot-prefix (defaults to the repo basename).

Validation

Before generating an archive, the function validates:

  1. Format enabled: The format's bit must be set in the snapshot mask
  2. Reference exists: The ref must resolve to a valid git object
  3. Object type: Must be a commit, tag, or tree

Archive Generation: write_archive_type()

static int write_archive_type(const char *format, const char *hex,
                               const char *prefix)
{
    struct archiver_args args;
    memset(&args, 0, sizeof(args));
    args.base = prefix;  /* directory prefix in archive */
    /* resolve hex to tree object */
    /* call write_archive() from libgit */
}

The actual archive creation is delegated to Git's write_archive() API, which handles tar and zip generation natively.

Compression Pipeline

For compressed formats, the archive data is piped through compression:

static int write_tar_gzip_archive(/* ... */)
{
    /* pipe tar output through gzip compression */
}

static int write_tar_bzip2_archive(/* ... */)
{
    /* pipe tar output through bzip2 compression */
}

static int write_tar_xz_archive(/* ... */)
{
    /* pipe tar output through xz compression */
}

static int write_tar_zstd_archive(/* ... */)
{
    /* pipe tar output through zstd compression */
}

static int write_tar_lzip_archive(/* ... */)
{
    /* pipe tar output through lzip compression */
}

HTTP Response

Snapshot responses include:

Content-Type: application/x-gzip
Content-Disposition: inline; filename="myrepo-v1.0.tar.gz"

The Content-Disposition header triggers a file download in browsers with the correct filename.

Snapshot Links

Snapshot links on repository pages are generated by ui-shared.c:

void cgit_print_snapshot_links(const char *repo, const char *head,
                               const char *hex, int snapshots)
{
    for (f = cgit_snapshot_formats; f->suffix; f++) {
        if (!(snapshots & f->bit))
            continue;
        /* generate link: repo/snapshot/prefix-ref.suffix */
    }
}

These links appear on the summary page and optionally in the log view.

Snapshot Prefix

The archive prefix (directory name inside the archive) is determined by:

  1. repo.snapshot-prefix if set
  2. Otherwise, the repository basename

For a request like myrepo-v1.0.tar.gz, the archive contains files under myrepo-v1.0/.

Signature Detection

cgit can detect and display signature files alongside snapshots. When a file matching <snapshot-name>.asc or <snapshot-name>.sig exists in the repository, a signature link is shown next to the snapshot download.

Configuration

Directive Default Description
snapshots (none) Space-separated list of enabled suffixes
repo.snapshots (inherited) Per-repo override
repo.snapshot-prefix (basename) Per-repo archive prefix
cache-snapshot-ttl 5 min Cache TTL for snapshot pages

Enabling Snapshots

# Global: enable tar.gz and zip for all repos
snapshots=tar.gz zip

# Per-repo: enable all formats
repo.url=myrepo
repo.snapshots=all

# Per-repo: disable snapshots
repo.url=internal-tools
repo.snapshots=

Security Considerations

  • Snapshots are generated on-the-fly from git objects, so they always reflect the repository's current state
  • Large repositories can produce large archives — consider enabling caching and setting appropriate max-blob-size limits
  • Snapshot requests for non-existent refs return a 404 error page
  • The snapshot filename is sanitized to prevent path traversal

Was this handbook page helpful?

This page is part of the Project Tick Handbook, which is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license. View full license details.
Last updated: April 18, 2026