If you’ve ever wished Gerrit supported git reflog
-style history natively, you’re not alone. While Gerrit keeps track of patch sets and reviews, it doesn’t provide a clean way to trace every ref update over time across all branches. To solve this gap, I built a plugin: gerrit-plugin-reflog-recorder.
What It Does
The plugin listens to every GitReferenceUpdatedListener
event and logs it into an embedded H2 database. Each entry contains:
- Project name
- Reference (e.g.,
refs/heads/main
) - Old revision SHA-1
- New revision SHA-1
- UTC timestamp
- Gerrit user
This allows you to answer questions like:
What was the state of
main
at 10:00 UTC yesterday?
Why Not Use Gerrit’s Built-in Reflog API?
Gerrit does provide a reflog endpoint:
GET /projects/{project}/branches/{branch}/reflog
However, it has significant limitations:
- It only works if Git reflogs are enabled and retained (not default in JGit-based Gerrit).
- It shows recent ref changes but doesn’t allow querying by timestamp.
- It’s tied to file-based Git storage and may expire due to garbage collection.
By contrast, this plugin:
- Stores updates in an indexed, queryable database (H2)
- Supports timestamp-based resolution
- Is independent of Git’s internal reflog retention
- Powers manifest rewriting for reproducible builds
Key Features
- Reflog-style tracking of all Git ref updates in Gerrit
- SSH commands to query historical revision state or resolve a manifest
- Manifest resolver that rewrites
default.xml
to match the state of the repo at a given timestamp
SSH Commands
get-reflog
: Returns the revision of a ref at a specific timeimport-native-reflog
: Imports JGit reflog from existing local repositoriesresolve-manifest
: Rewritesdefault.xml
using reflog data to create a timestamp-pinned manifest
Manifest Cleanup
When resolving manifests, the plugin strips:
<superproject>
and<contactinfo>
elements- Projects with
groups="notdefault"
The result mimics the output of:
repo manifest -r -o static.xml
Architecture
The plugin is structured in layers:
ReflogRepository
: Handles database operationsReflogService
: Implements lookup and resolution logicManifestResolver
: DOM manipulator for manifest editing- SSH commands: CLI tools for interacting with the data
ReflogRecorder
: The listener that writes to the database
Install and Use
To install:
git clone https://github.com/maksonlee/gerrit-plugin-reflog-recorder.git
cd gerrit-plugin-reflog-recorder
mvn package
cp target/reflog-recorder.jar $GERRIT_SITE/plugins/
sudo systemctl restart gerrit
To query:
ssh -p 29418 user@host gerrit get-reflog \
--project my-project \
--ref refs/heads/main \
--timestamp 2025-05-11T12:00:00Z
Sync Code Using Static Manifest
# Init repo with your manifest repo and branch
repo init -u git://gerrit/platform/manifest -b android-15.0.0_r30
# Override manifest with a pinned version generated from your plugin
ssh -p 29418 maksonlee@gerrit reflog-recorder resolve-manifest \
platform/manifest \
refs/heads/android-15.0.0_r30 \
2025-05-04T08:30:00Z > .repo/manifest.xml
# Sync code to exact state
repo sync -c
Future Plans
- Add REST APIs
- Add automated tests (unit and integration)
Source
You can find the full source code here: 👉 https://github.com/maksonlee/gerrit-plugin-reflog-recorder
If you’re using Gerrit for Android or AOSP workflows where pinned manifests are crucial, this plugin may help you track historical state with more confidence.
Let me know what you’d improve or add next!