mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
feat: add remote management functionality
- Introduced a new route for adding remotes to git worktrees. - Enhanced the PushToRemoteDialog component to support adding new remotes, including form handling and error management. - Updated the API client to include an endpoint for adding remotes. - Modified the worktree state management to track the presence of remotes. - Improved the list branches handler to check for configured remotes. This update allows users to easily add remotes through the UI, enhancing the overall git workflow experience.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
@@ -19,7 +20,7 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { toast } from 'sonner';
|
||||
import { Upload, RefreshCw, AlertTriangle, Sparkles } from 'lucide-react';
|
||||
import { Upload, RefreshCw, AlertTriangle, Sparkles, Plus, Link } from 'lucide-react';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import type { WorktreeInfo } from '../worktree-panel/types';
|
||||
|
||||
@@ -30,6 +31,16 @@ interface RemoteInfo {
|
||||
|
||||
const logger = createLogger('PushToRemoteDialog');
|
||||
|
||||
/**
|
||||
* Extracts error message from unknown error type
|
||||
*/
|
||||
function getErrorMessage(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
return String(error);
|
||||
}
|
||||
|
||||
interface PushToRemoteDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
@@ -49,6 +60,13 @@ export function PushToRemoteDialog({
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Add remote form state
|
||||
const [showAddRemoteForm, setShowAddRemoteForm] = useState(false);
|
||||
const [newRemoteName, setNewRemoteName] = useState('origin');
|
||||
const [newRemoteUrl, setNewRemoteUrl] = useState('');
|
||||
const [isAddingRemote, setIsAddingRemote] = useState(false);
|
||||
const [addRemoteError, setAddRemoteError] = useState<string | null>(null);
|
||||
|
||||
// Fetch remotes when dialog opens
|
||||
useEffect(() => {
|
||||
if (open && worktree) {
|
||||
@@ -61,6 +79,10 @@ export function PushToRemoteDialog({
|
||||
if (!open) {
|
||||
setSelectedRemote('');
|
||||
setError(null);
|
||||
setShowAddRemoteForm(false);
|
||||
setNewRemoteName('origin');
|
||||
setNewRemoteUrl('');
|
||||
setAddRemoteError(null);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
@@ -73,6 +95,36 @@ export function PushToRemoteDialog({
|
||||
}
|
||||
}, [remotes, selectedRemote]);
|
||||
|
||||
// Show add remote form when no remotes
|
||||
useEffect(() => {
|
||||
if (!isLoading && remotes.length === 0) {
|
||||
setShowAddRemoteForm(true);
|
||||
}
|
||||
}, [isLoading, remotes.length]);
|
||||
|
||||
/**
|
||||
* Transforms API remote data to RemoteInfo format
|
||||
*/
|
||||
const transformRemoteData = useCallback(
|
||||
(remotes: Array<{ name: string; url: string }>): RemoteInfo[] => {
|
||||
return remotes.map((r) => ({
|
||||
name: r.name,
|
||||
url: r.url,
|
||||
}));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Updates remotes state and hides add form if remotes exist
|
||||
*/
|
||||
const updateRemotesState = useCallback((remoteInfos: RemoteInfo[]) => {
|
||||
setRemotes(remoteInfos);
|
||||
if (remoteInfos.length > 0) {
|
||||
setShowAddRemoteForm(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchRemotes = async () => {
|
||||
if (!worktree) return;
|
||||
|
||||
@@ -84,21 +136,14 @@ export function PushToRemoteDialog({
|
||||
const result = await api.worktree.listRemotes(worktree.path);
|
||||
|
||||
if (result.success && result.result) {
|
||||
// Extract just the remote info (name and URL), not the branches
|
||||
const remoteInfos: RemoteInfo[] = result.result.remotes.map((r) => ({
|
||||
name: r.name,
|
||||
url: r.url,
|
||||
}));
|
||||
setRemotes(remoteInfos);
|
||||
if (remoteInfos.length === 0) {
|
||||
setError('No remotes found in this repository. Please add a remote first.');
|
||||
}
|
||||
const remoteInfos = transformRemoteData(result.result.remotes);
|
||||
updateRemotesState(remoteInfos);
|
||||
} else {
|
||||
setError(result.error || 'Failed to fetch remotes');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Failed to fetch remotes:', err);
|
||||
setError('Failed to fetch remotes');
|
||||
setError(getErrorMessage(err));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -115,47 +160,270 @@ export function PushToRemoteDialog({
|
||||
const result = await api.worktree.listRemotes(worktree.path);
|
||||
|
||||
if (result.success && result.result) {
|
||||
const remoteInfos: RemoteInfo[] = result.result.remotes.map((r) => ({
|
||||
name: r.name,
|
||||
url: r.url,
|
||||
}));
|
||||
setRemotes(remoteInfos);
|
||||
const remoteInfos = transformRemoteData(result.result.remotes);
|
||||
updateRemotesState(remoteInfos);
|
||||
toast.success('Remotes refreshed');
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to refresh remotes');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Failed to refresh remotes:', err);
|
||||
toast.error('Failed to refresh remotes');
|
||||
toast.error(getErrorMessage(err));
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddRemote = async () => {
|
||||
if (!worktree || !newRemoteName.trim() || !newRemoteUrl.trim()) return;
|
||||
|
||||
setIsAddingRemote(true);
|
||||
setAddRemoteError(null);
|
||||
|
||||
try {
|
||||
const api = getHttpApiClient();
|
||||
const result = await api.worktree.addRemote(
|
||||
worktree.path,
|
||||
newRemoteName.trim(),
|
||||
newRemoteUrl.trim()
|
||||
);
|
||||
|
||||
if (result.success && result.result) {
|
||||
toast.success(result.result.message);
|
||||
// Add the new remote to the list and select it
|
||||
const newRemote: RemoteInfo = {
|
||||
name: result.result.remoteName,
|
||||
url: result.result.remoteUrl,
|
||||
};
|
||||
setRemotes((prev) => [...prev, newRemote]);
|
||||
setSelectedRemote(newRemote.name);
|
||||
setShowAddRemoteForm(false);
|
||||
setNewRemoteName('origin');
|
||||
setNewRemoteUrl('');
|
||||
} else {
|
||||
setAddRemoteError(result.error || 'Failed to add remote');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Failed to add remote:', err);
|
||||
setAddRemoteError(getErrorMessage(err));
|
||||
} finally {
|
||||
setIsAddingRemote(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!worktree || !selectedRemote) return;
|
||||
onConfirm(worktree, selectedRemote);
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const renderAddRemoteForm = () => (
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-2">
|
||||
<Link className="w-4 h-4" />
|
||||
<span className="text-sm">
|
||||
{remotes.length === 0
|
||||
? 'No remotes found. Add a remote to push your branch.'
|
||||
: 'Add a new remote'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="remote-name">Remote Name</Label>
|
||||
<Input
|
||||
id="remote-name"
|
||||
placeholder="origin"
|
||||
value={newRemoteName}
|
||||
onChange={(e) => {
|
||||
setNewRemoteName(e.target.value);
|
||||
setAddRemoteError(null);
|
||||
}}
|
||||
disabled={isAddingRemote}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="remote-url">Remote URL</Label>
|
||||
<Input
|
||||
id="remote-url"
|
||||
placeholder="https://github.com/user/repo.git"
|
||||
value={newRemoteUrl}
|
||||
onChange={(e) => {
|
||||
setNewRemoteUrl(e.target.value);
|
||||
setAddRemoteError(null);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (
|
||||
e.key === 'Enter' &&
|
||||
newRemoteName.trim() &&
|
||||
newRemoteUrl.trim() &&
|
||||
!isAddingRemote
|
||||
) {
|
||||
handleAddRemote();
|
||||
}
|
||||
}}
|
||||
disabled={isAddingRemote}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Supports HTTPS, SSH (git@github.com:user/repo.git), or git:// URLs
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{addRemoteError && (
|
||||
<div className="flex items-center gap-2 text-destructive">
|
||||
<AlertTriangle className="w-4 h-4" />
|
||||
<span className="text-sm">{addRemoteError}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderRemoteSelector = () => (
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="remote-select">Select Remote</Label>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setShowAddRemoteForm(true)}
|
||||
className="h-6 px-2 text-xs"
|
||||
>
|
||||
<Plus className="w-3 h-3 mr-1" />
|
||||
Add
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleRefresh}
|
||||
disabled={isRefreshing}
|
||||
className="h-6 px-2 text-xs"
|
||||
>
|
||||
{isRefreshing ? (
|
||||
<Spinner size="xs" className="mr-1" />
|
||||
) : (
|
||||
<RefreshCw className="w-3 h-3 mr-1" />
|
||||
)}
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Select value={selectedRemote} onValueChange={setSelectedRemote}>
|
||||
<SelectTrigger id="remote-select">
|
||||
<SelectValue placeholder="Select a remote" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{remotes.map((remote) => (
|
||||
<SelectItem key={remote.name} value={remote.name}>
|
||||
<div className="flex flex-col items-start">
|
||||
<span className="font-medium">{remote.name}</span>
|
||||
<span className="text-xs text-muted-foreground truncate max-w-[300px]">
|
||||
{remote.url}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{selectedRemote && (
|
||||
<div className="mt-2 p-3 rounded-md bg-muted/50 border border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This will create a new remote branch{' '}
|
||||
<span className="font-mono text-foreground">
|
||||
{selectedRemote}/{worktree?.branch}
|
||||
</span>{' '}
|
||||
and set up tracking.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderFooter = () => {
|
||||
if (showAddRemoteForm) {
|
||||
return (
|
||||
<DialogFooter>
|
||||
{remotes.length > 0 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShowAddRemoteForm(false)}
|
||||
disabled={isAddingRemote}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isAddingRemote}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleAddRemote}
|
||||
disabled={!newRemoteName.trim() || !newRemoteUrl.trim() || isAddingRemote}
|
||||
>
|
||||
{isAddingRemote ? (
|
||||
<>
|
||||
<Spinner size="sm" className="mr-2" />
|
||||
Adding...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Add Remote
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleConfirm} disabled={!selectedRemote || isLoading}>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
Push to {selectedRemote || 'Remote'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-[450px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Upload className="w-5 h-5 text-primary" />
|
||||
Push New Branch to Remote
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium bg-primary/10 text-primary px-2 py-0.5 rounded-full ml-2">
|
||||
<Sparkles className="w-3 h-3" />
|
||||
new
|
||||
</span>
|
||||
{showAddRemoteForm ? (
|
||||
<>
|
||||
<Plus className="w-5 h-5 text-primary" />
|
||||
Add Remote
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Upload className="w-5 h-5 text-primary" />
|
||||
Push New Branch to Remote
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium bg-primary/10 text-primary px-2 py-0.5 rounded-full ml-2">
|
||||
<Sparkles className="w-3 h-3" />
|
||||
new
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Push{' '}
|
||||
<span className="font-mono text-foreground">
|
||||
{worktree?.branch || 'current branch'}
|
||||
</span>{' '}
|
||||
to a remote repository for the first time.
|
||||
{showAddRemoteForm ? (
|
||||
<>Add a remote repository to push your changes to.</>
|
||||
) : (
|
||||
<>
|
||||
Push{' '}
|
||||
<span className="font-mono text-foreground">
|
||||
{worktree?.branch || 'current branch'}
|
||||
</span>{' '}
|
||||
to a remote repository for the first time.
|
||||
</>
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -163,7 +431,7 @@ export function PushToRemoteDialog({
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Spinner size="lg" />
|
||||
</div>
|
||||
) : error ? (
|
||||
) : error && !showAddRemoteForm ? (
|
||||
<div className="flex flex-col items-center gap-4 py-6">
|
||||
<div className="flex items-center gap-2 text-destructive">
|
||||
<AlertTriangle className="w-5 h-5" />
|
||||
@@ -174,68 +442,13 @@ export function PushToRemoteDialog({
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
) : showAddRemoteForm ? (
|
||||
renderAddRemoteForm()
|
||||
) : (
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="remote-select">Select Remote</Label>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleRefresh}
|
||||
disabled={isRefreshing}
|
||||
className="h-6 px-2 text-xs"
|
||||
>
|
||||
{isRefreshing ? (
|
||||
<Spinner size="xs" className="mr-1" />
|
||||
) : (
|
||||
<RefreshCw className="w-3 h-3 mr-1" />
|
||||
)}
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
<Select value={selectedRemote} onValueChange={setSelectedRemote}>
|
||||
<SelectTrigger id="remote-select">
|
||||
<SelectValue placeholder="Select a remote" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{remotes.map((remote) => (
|
||||
<SelectItem key={remote.name} value={remote.name}>
|
||||
<div className="flex flex-col items-start">
|
||||
<span className="font-medium">{remote.name}</span>
|
||||
<span className="text-xs text-muted-foreground truncate max-w-[300px]">
|
||||
{remote.url}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{selectedRemote && (
|
||||
<div className="mt-2 p-3 rounded-md bg-muted/50 border border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This will create a new remote branch{' '}
|
||||
<span className="font-mono text-foreground">
|
||||
{selectedRemote}/{worktree?.branch}
|
||||
</span>{' '}
|
||||
and set up tracking.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
renderRemoteSelector()
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleConfirm} disabled={!selectedRemote || isLoading}>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
Push to {selectedRemote || 'Remote'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
{renderFooter()}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
Copy,
|
||||
Eye,
|
||||
ScrollText,
|
||||
Sparkles,
|
||||
CloudOff,
|
||||
Terminal,
|
||||
SquarePlus,
|
||||
SplitSquareHorizontal,
|
||||
@@ -365,9 +365,9 @@ export function WorktreeActionsDropdown({
|
||||
{isPushing ? 'Pushing...' : 'Push'}
|
||||
{!canPerformGitOps && <AlertCircle className="w-3 h-3 ml-auto text-muted-foreground" />}
|
||||
{canPerformGitOps && !hasRemoteBranch && (
|
||||
<span className="ml-auto inline-flex items-center gap-0.5 text-[10px] bg-primary/20 text-primary px-1.5 py-0.5 rounded">
|
||||
<Sparkles className="w-2.5 h-2.5" />
|
||||
new
|
||||
<span className="ml-auto inline-flex items-center gap-0.5 text-[10px] bg-amber-500/20 text-amber-600 dark:text-amber-400 px-1.5 py-0.5 rounded">
|
||||
<CloudOff className="w-2.5 h-2.5" />
|
||||
local only
|
||||
</span>
|
||||
)}
|
||||
{canPerformGitOps && hasRemoteBranch && aheadCount > 0 && (
|
||||
|
||||
@@ -151,7 +151,7 @@ export function useWorktreeDiffs(projectPath: string | undefined, featureId: str
|
||||
interface BranchInfo {
|
||||
name: string;
|
||||
isCurrent: boolean;
|
||||
isRemote?: boolean;
|
||||
isRemote: boolean;
|
||||
lastCommit?: string;
|
||||
upstream?: string;
|
||||
}
|
||||
@@ -161,6 +161,7 @@ interface BranchesResult {
|
||||
aheadCount: number;
|
||||
behindCount: number;
|
||||
hasRemoteBranch: boolean;
|
||||
hasAnyRemotes: boolean;
|
||||
isGitRepo: boolean;
|
||||
hasCommits: boolean;
|
||||
}
|
||||
@@ -188,6 +189,7 @@ export function useWorktreeBranches(worktreePath: string | undefined, includeRem
|
||||
aheadCount: 0,
|
||||
behindCount: 0,
|
||||
hasRemoteBranch: false,
|
||||
hasAnyRemotes: false,
|
||||
isGitRepo: false,
|
||||
hasCommits: false,
|
||||
};
|
||||
@@ -198,6 +200,7 @@ export function useWorktreeBranches(worktreePath: string | undefined, includeRem
|
||||
aheadCount: 0,
|
||||
behindCount: 0,
|
||||
hasRemoteBranch: false,
|
||||
hasAnyRemotes: result.result?.hasAnyRemotes ?? false,
|
||||
isGitRepo: true,
|
||||
hasCommits: false,
|
||||
};
|
||||
@@ -212,6 +215,7 @@ export function useWorktreeBranches(worktreePath: string | undefined, includeRem
|
||||
aheadCount: result.result?.aheadCount ?? 0,
|
||||
behindCount: result.result?.behindCount ?? 0,
|
||||
hasRemoteBranch: result.result?.hasRemoteBranch ?? false,
|
||||
hasAnyRemotes: result.result?.hasAnyRemotes ?? false,
|
||||
isGitRepo: true,
|
||||
hasCommits: true,
|
||||
};
|
||||
|
||||
@@ -1782,6 +1782,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
aheadCount: 2,
|
||||
behindCount: 0,
|
||||
hasRemoteBranch: true,
|
||||
hasAnyRemotes: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1878,6 +1878,8 @@ export class HttpApiClient implements ElectronAPI {
|
||||
this.post('/api/worktree/switch-branch', { worktreePath, branchName }),
|
||||
listRemotes: (worktreePath: string) =>
|
||||
this.post('/api/worktree/list-remotes', { worktreePath }),
|
||||
addRemote: (worktreePath: string, remoteName: string, remoteUrl: string) =>
|
||||
this.post('/api/worktree/add-remote', { worktreePath, remoteName, remoteUrl }),
|
||||
openInEditor: (worktreePath: string, editorCommand?: string) =>
|
||||
this.post('/api/worktree/open-in-editor', { worktreePath, editorCommand }),
|
||||
getDefaultEditor: () => this.get('/api/worktree/default-editor'),
|
||||
|
||||
18
apps/ui/src/types/electron.d.ts
vendored
18
apps/ui/src/types/electron.d.ts
vendored
@@ -951,6 +951,7 @@ export interface WorktreeAPI {
|
||||
aheadCount: number;
|
||||
behindCount: number;
|
||||
hasRemoteBranch: boolean;
|
||||
hasAnyRemotes: boolean;
|
||||
};
|
||||
error?: string;
|
||||
code?: 'NOT_GIT_REPO' | 'NO_COMMITS'; // Error codes for git status issues
|
||||
@@ -988,6 +989,23 @@ export interface WorktreeAPI {
|
||||
code?: 'NOT_GIT_REPO' | 'NO_COMMITS';
|
||||
}>;
|
||||
|
||||
// Add a new remote to a git repository
|
||||
addRemote: (
|
||||
worktreePath: string,
|
||||
remoteName: string,
|
||||
remoteUrl: string
|
||||
) => Promise<{
|
||||
success: boolean;
|
||||
result?: {
|
||||
remoteName: string;
|
||||
remoteUrl: string;
|
||||
fetched: boolean;
|
||||
message: string;
|
||||
};
|
||||
error?: string;
|
||||
code?: 'REMOTE_EXISTS';
|
||||
}>;
|
||||
|
||||
// Open a worktree directory in the editor
|
||||
openInEditor: (
|
||||
worktreePath: string,
|
||||
|
||||
Reference in New Issue
Block a user