Query your Git commit history using LINQ-like syntax directly from the command line.
- 🔍 LINQ-like Query Syntax - Use familiar C# LINQ methods to query commits
- 🎯 Interactive Mode - REPL with autocomplete and command history
- ⚡ CLI Mode - Execute single queries for scripting and automation
- 📊 Rich Output - Beautiful tables powered by Spectre.Console
- 🔧 Extensible Commands - Built-in help, examples, and history commands
- .NET 8.0 SDK or later
dotnet tool install --global GitLinqgit clone https://github.com/williamguilhermesouza/gitlinq.git
cd gitlinq/src
dotnet build
dotnet runcd src
dotnet pack -c Release
dotnet tool install --global --add-source ./bin/Release GitLinqSimply run gitlinq inside any Git repository:
gitlinqYou'll enter an interactive REPL where you can type queries:
GitLinq - Query git commits using LINQ-like syntax
Type 'help' for available commands or enter a query.
gitlinq> Commits.Take(5)
Execute a single query and exit:
gitlinq -q "Commits.Take(10)"
gitlinq --query "Commits.Where(c => c.Message.Contains(\"fix\"))"| Option | Description |
|---|---|
-q, --query <query> |
Execute a single query and exit |
-h, --help |
Show help message |
| Variable | Description |
|---|---|
GITLINQ_DEBUG=1 |
Enable debug mode for troubleshooting terminal/encoding issues |
GitLinq supports a LINQ-like query syntax to filter, transform, and aggregate commits.
| Source | Description |
|---|---|
Commits |
Full commit information including diff data via the Diff property |
| Property | Type | Description |
|---|---|---|
Sha |
string | Full commit SHA hash |
Message |
string | Full commit message |
MessageShort |
string | First line of commit message |
AuthorName |
string | Author's name |
AuthorEmail |
string | Author's email |
AuthorWhen |
DateTimeOffset | Author timestamp |
Diff |
DiffData | File changes, lines added/deleted for this commit |
| Property | Type | Description |
|---|---|---|
Files |
List<FileChange> | List of changed files |
TotalLinesAdded |
int | Sum of lines added across all files |
TotalLinesDeleted |
int | Sum of lines deleted across all files |
FilesChanged |
int | Number of files changed |
| Property | Type | Description |
|---|---|---|
Path |
string | File path |
OldPath |
string? | Previous path (for renamed files) |
Status |
string | Change type: Added, Deleted, Modified, Renamed |
LinesAdded |
int | Lines added in this file |
LinesDeleted |
int | Lines deleted in this file |
IsBinary |
bool | Whether file is binary |
AddedContent |
List<string> | Actual text lines that were added |
DeletedContent |
List<string> | Actual text lines that were deleted |
| Method | Description | Example |
|---|---|---|
AddedContains(text) |
Check if any added line contains text (case-insensitive) | f.AddedContains("TODO") |
DeletedContains(text) |
Check if any deleted line contains text (case-insensitive) | f.DeletedContains("bug") |
ContentContains(text) |
Check if any changed line (added or deleted) contains text | f.ContentContains("password") |
| Method | Description | Example |
|---|---|---|
Take(n) |
Get first n commits | Commits.Take(10) |
Skip(n) |
Skip first n commits | Commits.Skip(5) |
First() |
Get first commit | Commits.First() |
First(predicate) |
Get first matching commit | Commits.First(c => c.Message.Contains("fix")) |
FirstOrDefault() |
Get first commit or null | Commits.FirstOrDefault() |
FirstOrDefault(predicate) |
Get first matching or null | Commits.FirstOrDefault(c => c.AuthorName.Contains("Alice")) |
Where(predicate) |
Filter commits | Commits.Where(c => c.Message.Contains("feat")) |
Select(selector) |
Project commits | Commits.Select(c => c.Message) |
OrderBy(selector) |
Sort ascending | Commits.OrderBy(c => c.AuthorName) |
OrderByDescending(selector) |
Sort descending | Commits.OrderByDescending(c => c.AuthorWhen) |
Count() |
Count all commits | Commits.Count() |
Count(predicate) |
Count matching commits | Commits.Count(c => c.AuthorName.Contains("Bob")) |
Any() |
Check if commits exist | Commits.Any() |
Any(predicate) |
Check if any match | Commits.Any(c => c.Message.Contains("hotfix")) |
| Method | Example |
|---|---|
Contains(text) |
c.Message.Contains("fix") |
StartsWith(text) |
c.Message.StartsWith("feat") |
EndsWith(text) |
c.Message.EndsWith("!") |
| Operator | Example |
|---|---|
> |
d.FilesChanged > 5 |
< |
d.TotalLinesAdded < 100 |
>= |
d.FilesChanged >= 3 |
<= |
d.TotalLinesDeleted <= 10 |
== |
d.FilesChanged == 1 |
!= |
d.FilesChanged != 0 |
# Get all commits
Commits
# Get the first 10 commits
Commits.Take(10)
# Pagination: skip 5, take 10
Commits.Skip(5).Take(10)
# Get the most recent commit
Commits.First()
# Count total commits
Commits.Count()
# Find commits with 'fix' in message
Commits.Where(c => c.Message.Contains("fix"))
# Find commits by author
Commits.Where(c => c.AuthorName.Contains("Alice"))
# Find commits starting with 'feat' (conventional commits)
Commits.Where(c => c.Message.StartsWith("feat"))
# Find first commit mentioning 'bug'
Commits.First(c => c.Message.Contains("bug"))
# Check if any hotfix commits exist
Commits.Any(c => c.Message.Contains("hotfix"))
# Count commits by a specific author
Commits.Count(c => c.AuthorName.Contains("Bob"))
# Chain multiple operations
Commits.Where(c => c.AuthorName.Contains("Alice")).Take(5)# Find commits that changed more than 5 files
Commits.Where(c => c.Diff.FilesChanged > 5)
# Find commits with more than 100 lines added
Commits.Where(c => c.Diff.TotalLinesAdded > 100).Take(10)
# Find commits with significant deletions
Commits.Where(c => c.Diff.TotalLinesDeleted >= 50)
# Get files changed in the most recent commit
Commits.First().Diff.Files
# Find single-file commits
Commits.Where(c => c.Diff.FilesChanged == 1).Take(10)
# Find large refactoring commits (many files, many changes)
Commits.Where(c => c.Diff.FilesChanged >= 10).Take(5)
# Combine commit message filtering with diff statistics
Commits.Where(c => c.Message.Contains("refactor")).Where(c => c.Diff.FilesChanged > 3)# Find commits that added a TODO comment
Commits.Where(c => c.Diff.Files.Any(f => f.AddedContains("TODO")))
# Find commits that removed code containing "bug"
Commits.Where(c => c.Diff.Files.Any(f => f.DeletedContains("bug")))
# Find commits that touched code containing "password" (added or deleted)
Commits.Where(c => c.Diff.Files.Any(f => f.ContentContains("password")))
# Find commits that added "console.log" (for debugging cleanup)
Commits.Where(c => c.Diff.Files.Any(f => f.AddedContains("console.log")))
# Find commits where a specific function was modified
Commits.Where(c => c.Diff.Files.Any(f => f.ContentContains("myFunction")))
# View the actual added lines in the most recent commit's first file
Commits.First().Diff.Files.First().AddedContent| Command | Aliases | Description |
|---|---|---|
help |
h, ? |
Show available commands |
examples |
ex, samples |
Show example queries |
history |
hist |
Show command history |
clear |
cls |
Clear the screen |
exit |
quit, q |
Exit GitLinq |
src/
├── Program.cs # Entry point, CLI handling, REPL loop
├── QueryParser.cs # Sprache-based LINQ parser
├── LinqExpressionBuilder.cs # AST to LINQ Expression converter
├── AutoCompletionHandler.cs # Tab completion for REPL
├── AST/ # Abstract Syntax Tree nodes
│ ├── BaseNode.cs
│ ├── IdentifierNode.cs
│ ├── StringLiteralNode.cs
│ ├── NumberLiteralNode.cs
│ ├── MemberAccessNode.cs
│ ├── MethodCallNode.cs
│ ├── LambdaNode.cs
│ └── BinaryNode.cs
├── Commands/ # Interactive command system
│ ├── ICommand.cs
│ ├── CommandRegistry.cs
│ ├── HelpCommand.cs
│ ├── ExamplesCommand.cs
│ ├── HistoryCommand.cs
│ ├── Clear.cs
│ └── ExitCommand.cs
├── Diagnostics/ # Debug and troubleshooting utilities
│ └── DebugHelper.cs # Environment info, terminal detection
├── Models/ # Domain models
│ ├── CommitInfo.cs # Git commit data
│ ├── DiffData.cs # Diff statistics and content search
│ ├── FileChange.cs # Individual file change data
│ └── MatchedLine.cs # Matched content with context
├── Services/ # External service integrations
│ └── GitService.cs # Git repository operations (LibGit2Sharp)
└── UI/ # User interface components
├── ResultDisplay.cs # Query result rendering (tables, content)
└── HelpDisplay.cs # CLI help output
- LibGit2Sharp - Git repository access
- Sprache - Parser combinator library
- Spectre.Console - Beautiful console output
- ReadLine - GNU Readline-like input with history
dotnet testsrc/- Main application source codeAST/- Abstract Syntax Tree node typesCommands/- REPL command implementationsDiagnostics/- Debug and troubleshooting utilitiesModels/- Domain models (CommitInfo, DiffData, etc.)Services/- External integrations (Git via LibGit2Sharp)UI/- Display and rendering components
tests/- Unit tests for parser and expression builder
If you encounter issues (especially on Windows PowerShell or CMD), enable debug mode:
# Windows PowerShell
$env:GITLINQ_DEBUG="1"
gitlinq
# Windows CMD
set GITLINQ_DEBUG=1
gitlinq
# Linux/macOS
GITLINQ_DEBUG=1 gitlinqDebug mode displays:
- OS and .NET runtime information
- Console encoding settings (important for quote handling)
- Terminal type detection
- Input byte analysis (helps identify encoding issues)
Common Issues:
- "No commits found" when using Contains(): This was caused by null bytes being inserted by the ReadLine library on some Windows terminals. Fixed in v0.0.2+.
- Garbled output: Ensure your terminal supports UTF-8. The tool sets UTF-8 encoding automatically.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Found a bug or have a feature request?
- GitHub Issues: Open an issue on the repository
- Email: Contact me directly at williamguilhermesouza@gmail.com
When reporting bugs, please include:
- GitLinq version (
dotnet tool list -g | Select-String GitLinq) - Operating system and terminal being used
- Steps to reproduce the issue
- Any error messages or unexpected output
This project is licensed under the MIT License - see the LICENSE file for details.
- Support for branches and tags queries
- Date range filtering (
c.AuthorWhen > "2024-01-01") - File change statistics per commit
- Export results to JSON/CSV
- Configuration file support
- More LINQ methods (
Last,Single,Distinct,GroupBy)
Made by William Guilherme using .NET 8.0
