Chapi Assistant provides a comprehensive Git workflow with visual change tracking, intelligent commit generation, and seamless remote synchronization.
Overview
Chapi’s Git integration includes:
Real-time change detection with file watching
Visual diff viewer using DiffPlex
AI-generated commit messages following Conventional Commits
Stash management for work-in-progress changes
Branch operations with conflict detection
Remote authentication for GitHub and GitLab
Viewing Changes
Chapi automatically detects file changes in your repository.
Change Detection
Chapi watches your repository for changes using GitChangeWatcher: // Automatic change detection
_changeWatcher . WatchRepository ( projectPath );
_changeWatcher . RepositoryChanged += OnRepositoryChanged ;
Changes are cached for performance (similar to GitHub Desktop): 📊 Modified: 3 files
📝 Untracked: 1 file
➕ Additions: 150 lines
➖ Deletions: 45 lines
Change Types
Chapi displays different change statuses:
M - Modified: Existing file changed
A - Added: New file staged
D - Deleted: File removed
R - Renamed: File renamed or moved
? - Untracked: New file not yet added
U - Conflict: Merge conflict detected
Visual Diff
Click any file to see line-by-line changes: - Old line (deleted)
+ New line (added)
Unchanged line (context)
The diff viewer uses DiffPlex with intelligent context: var diffBuilder = new InlineDiffBuilder ( new Differ ());
var diff = diffBuilder . BuildDiffModel ( oldText , newText );
Making Commits
Manual Commit
AI-Generated Commit
Via AI Assistant
Create commits with custom messages:
Select Files
Choose which files to include in the commit:
Click individual files to select/deselect
Use “Select All” for all changes
Use “Deselect All” to clear selection
Write Commit Message
Follow Conventional Commits format: feat(auth): add OAuth2 authentication
Implement GitHub and GitLab OAuth providers with:
- Token storage and validation
- Automatic credential refresh
- Avatar caching service
Summary : type(scope): brief description (max 72 chars)Description : Detailed explanation with bullet points
Commit Changes
Click “Commit” to save your changes: var request = new CommitRequest
{
ProjectPath = projectPath ,
Message = $" { summary } \n\n { description } " ,
Files = selectedFiles
};
await _commitChangesUseCase . ExecuteAsync ( request );
Let AI analyze your changes and generate the commit message:
Select Changed Files
Select the files you want to commit (at least one required).
Generate Message
Click the “Generate with AI” button: await _generateCommitMessageUseCase . ExecuteAsync ( diffContent );
The AI analyzes the diff and returns: {
"summary" : "feat(git): implement AI commit generation" ,
"description" : "Add Gemini integration for commit messages. \n\n - Create GenerateCommitMessageUseCase \n - Integrate with ChangesViewModel \n - Add streaming response support"
}
Review and Commit
Review the AI-generated message, edit if needed, then commit.
Use natural language to commit: # Simple commit
"Commit all changes with message: Add user authentication"
# Commit and push
"Commit these changes and push to remote"
# AI-generated message
"Analyze my changes and create a professional commit"
The AI Assistant will:
Analyze modified and untracked files
Generate appropriate commit message
Execute commit
Optionally push to remote
Stash Management
Save work-in-progress changes without committing:
Create Stash
Select files and click “Stash Selected”: await _stashChangesUseCase . ExecuteAsync (
projectPath ,
message : "WIP: implementing OAuth" ,
files : selectedFiles
);
Result: ✅ Saved to stash: WIP: implementing OAuth
View Stashes
See all stashes for the current branch: stash@{0}: On main: WIP: implementing OAuth
stash@{1}: On main: Experimental feature
Click a stash to view its contents and diff.
Apply Stash
Restore stashed changes: // Pop stash (apply and remove)
await _stashPopUseCase . ExecuteAsync ( projectPath , index : 0 );
// Restore single file from stash
await _gitRepository . RestoreFileFromStashAsync (
projectPath , "stash@{0}" , filePath
);
Manage Stashes
Drop : Remove a specific stash
Clear : Remove all stashes
Restore File : Extract single file from stash
Branch Operations
Create Branch
Switch Branch
Merge Branch
Delete Branch
await _createBranchUseCase . ExecuteAsync (
projectPath ,
branchName : "feature/oauth-integration"
);
Via AI Assistant: "Create a new branch called feature/oauth-integration"
await _switchBranchUseCase . ExecuteAsync (
projectPath ,
branchName : "develop"
);
Via AI Assistant: "Switch to develop branch"
// Check for conflicts first
var ( hasConflicts , message ) = await _gitRepository
. CheckMergeConflictsAsync ( projectPath , sourceBranch );
if ( ! hasConflicts )
{
await _gitRepository . MergeBranchAsync (
projectPath ,
sourceBranch ,
fastForward : true
);
}
await _gitRepository . DeleteBranchAsync (
projectPath ,
branchName : "feature/completed" ,
force : false ,
deleteRemote : true
);
Remote Operations
Authentication
Chapi supports OAuth authentication for GitHub and GitLab:
Detect Provider
Chapi automatically detects the Git provider from remote URL: var provider = _authFactory . DetectProviderFromUrl ( remoteUrl );
// Returns: GitProvider.GitHub or GitProvider.GitLab
OAuth Flow
Click the provider icon to authenticate:
Open browser for OAuth authorization
Grant permissions to Chapi Assistant
Redirect to localhost callback
Exchange code for access token
Store credentials securely
var result = await provider . AuthenticateAsync ();
if ( result . IsSuccess )
{
await _credentialStorage . SaveCredentialAsync (
"GitHub" ,
result . Data . Username ,
result . Data . AccessToken
);
}
Avatar Display
After authentication, Chapi shows your avatar: // GitHub avatar
var avatarUrl = $"https://github.com/ { username } .png" ;
// GitLab avatar (via API)
var avatarUrl = await AvatarCacheService . Instance
. GetGitLabAvatarUrlAsync ( username );
Push and Pull
Push
Pull
Fetch
Sync Status
await _pushChangesUseCase . ExecuteAsync (
projectPath ,
branch : "main"
);
With authentication, credentials are used automatically: git push origin main
# Uses stored OAuth token
await _pullChangesUseCase . ExecuteAsync (
projectPath ,
branch : "main" ,
rebase : true
);
await _fetchChangesUseCase . ExecuteAsync (
projectPath ,
isSilent : false
);
Chapi shows sync indicators: ⬆️ Ahead: 3 commits (unpushed)
⬇️ Behind: 1 commit (not pulled)
var ( ahead , behind ) = await _gitRepository
. GetAheadBehindCountAsync ( projectPath );
Tag Management
Create and manage Git tags for releases:
// Create tag
await _createTagUseCase . ExecuteAsync (
projectPath ,
tagName : "v1.0.0" ,
message : "First stable release"
);
// Push tag to remote
await _gitRepository . PushTagAsync ( projectPath , "v1.0.0" );
// List all tags
var tags = await _gitRepository . GetTagsAsync ( projectPath );
Via AI Assistant:
"Create tag v1.0.0 with message: First stable release"
Advanced Operations
Reset Commit
Undo the last commit:
await _resetCommitUseCase . ExecuteAsync (
projectPath ,
mode : ResetMode . Soft // Keep changes in working directory
);
Reset modes:
Soft : Keep changes staged
Mixed : Keep changes unstaged
Hard : Discard all changes
Discard Changes
Revert uncommitted changes:
// Discard specific files
await _discardChangesUseCase . ExecuteAsync (
projectPath ,
files : new [] { "src/file.cs" }
);
// Discard all changes
await _discardChangesUseCase . ExecuteAsync (
projectPath ,
files : null
);
Rebase
Rebase current branch onto another:
await _gitRepository . RebaseBranchAsync (
projectPath ,
targetBranch : "main"
);
AI Assistant Integration
The AI Assistant understands Git context:
public string BuildContextInfo ( ConversationContext context )
{
var git = context . CurrentProject . Git ;
return $@"
=== GIT INFORMATION ===
🌿 Branch: { git . CurrentBranch }
📊 Sync: + { git . AheadBy } ahead, - { git . BehindBy } behind
⚠️ Uncommitted: { git . ModifiedFiles . Count } modified
Modified files:
{ string . Join ( " \n " , git . ModifiedFiles . Take ( 5 ))}
Recent commits:
{ string . Join ( " \n " , git . RecentCommits . Take ( 5 ))}
" ;
}
Ask questions like:
"What files did I change?"
"Explain my recent commits"
"What's the difference between my branch and main?"
"Do I have any merge conflicts?"
Best Practices
Make small, focused commits
Commit after each logical change
Don’t mix unrelated changes
Follow Conventional Commits:
feat: New feature
fix: Bug fix
refactor: Code refactoring
chore: Maintenance tasks
docs: Documentation changes
Use Stash for Context Switching
Stash before switching branches
Name stashes descriptively
Clean up old stashes regularly
Fetch/pull before starting work
Push completed features promptly
Resolve conflicts early
Chapi includes several Git performance optimizations:
// Change caching (like GitHub Desktop)
public class GitChangesCache
{
private Dictionary < string , CachedChanges > _cache = new ();
public bool TryGetChanges (
string projectPath ,
out List < FileChange > changes )
{
if ( _cache . TryGetValue ( projectPath , out var cached ))
{
changes = cached . Changes ;
return true ;
}
return false ;
}
}
// Background stats loading
private async Task LoadFileStatsInBackgroundAsync ()
{
// Load visible files first (top 20)
var visibleFiles = Changes . Take ( 20 );
foreach ( var file in visibleFiles )
{
var stats = await _gitRepository . GetFileStatsAsync (
projectPath , file . FilePath
);
file . Additions = stats . additions ;
file . Deletions = stats . deletions ;
}
// Then load remaining files
// ...
}
Troubleshooting
Authentication Failed : Ensure you’ve completed OAuth flow and tokens are valid. Re-authenticate if needed.
Merge Conflicts : Chapi detects conflicts but you must resolve them manually in your code editor.
All Git operations use the installed Git CLI. Ensure Git is installed and accessible in your PATH.
Next Steps
AI Setup Configure AI features for commit generation and assistance
Templates Learn about project templates and customization