A Node.js script that fetches all commit messages by a specific user across all accessible GitLab repositories, filtered by date range, and outputs the results as a CSV file.
- Scans all projects the authenticated user has access to
- Searches across all branches (not just the default branch)
- Filters commits by author email and date range
- Deduplicates commits that appear on multiple branches
- Streams CSV output incrementally (memory-safe for large instances)
- Progress and error messages go to
stderr, keepingstdoutclean for CSV
- Node.js v18+ (uses native
fetch) - A GitLab personal access token with
read_apiscope - GitLab CE v17.5.x or compatible
No dependencies required. Clone or download the script and run it directly.
# Download the script
curl -O https://your-host/gitlab-commits.mjs
# Or clone your repo
git clone https://your-repo/gitlab-commits-export.git
cd gitlab-commits-exportEdit the constants at the top of gitlab-commits.mjs:
const GITLAB_URL = 'https://your-gitlab.example.com'; // Your GitLab instance URL
const TOKEN = 'your_personal_access_token'; // GitLab PAT with read_api scope
const AUTHOR_EMAIL = 'user@example.com'; // Commit author email to filter by
const SINCE = '2024-01-01T00:00:00Z'; // Start of date range (ISO 8601)
const UNTIL = '2024-12-31T23:59:59Z'; // End of date range (ISO 8601)- Go to GitLab → User Settings → Access Tokens
- Create a token with the
read_apiscope - Copy the token and paste it into the
TOKENconstant
# Output CSV to terminal
node gitlab-commits.mjs
# Save CSV to file (progress shown in terminal)
node gitlab-commits.mjs > commits.csv
# Save CSV to file and also capture progress log
node gitlab-commits.mjs 2>progress.log > commits.csv
# Watch progress in terminal while saving CSV to file
node gitlab-commits.mjs 2>&1 1>commits.csv | catThe script outputs a CSV file with the following columns:
| Column | Description |
|---|---|
project |
Full project path (e.g. group/repo) |
short_id |
Abbreviated commit SHA (e.g. a1b2c3d4) |
id |
Full commit SHA |
date |
Authored date in ISO 8601 format |
title |
First line of the commit message |
message |
Full commit message |
project,short_id,id,date,title,message
group/my-repo,a1b2c3d4,a1b2c3d4e5f6...,2024-06-15T10:23:00.000+06:00,Fix null pointer in payment module,Fix null pointer in payment module
group/another-repo,b2c3d4e5,b2c3d4e5f6a7...,2024-06-20T14:05:00.000+06:00,Refactor invoice service,Refactor invoice service- Project scope: By default, the script fetches all projects where the authenticated user is a member (
membership=true). To restrict to only owned projects, changemembership: truetoowned: truein thefetchAllPagescall for projects. - Rate limiting: On GitLab instances with many repositories, consider adding a small delay between project iterations to avoid hitting API rate limits.
- Large instances: CSV is written incrementally to
stdoutas each project is processed, so memory usage stays low regardless of the number of commits found. - Author matching: Filtering is done by
author_emailin code rather than using GitLab's looseauthorquery parameter, which only matches on name and is unreliable.
| Symptom | Likely Cause | Fix |
|---|---|---|
API error 401 |
Invalid or expired token | Regenerate your PAT |
API error 403 |
Token missing read_api scope |
Recreate token with correct scope |
Skipping <project>: ... |
Repo is empty or archived | Expected — script skips gracefully |
| No commits found | Wrong AUTHOR_EMAIL |
Verify the email matches GitLab commit history |
| Duplicate rows | Should not happen — open an issue | Deduplication is done via commit SHA |
MIT