mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
fixes
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Download,
|
Download,
|
||||||
|
Upload,
|
||||||
GitBranchPlus,
|
GitBranchPlus,
|
||||||
Check,
|
Check,
|
||||||
Search,
|
Search,
|
||||||
@@ -65,9 +66,12 @@ export function WorktreeSelector({
|
|||||||
}: WorktreeSelectorProps) {
|
}: WorktreeSelectorProps) {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isPulling, setIsPulling] = useState(false);
|
const [isPulling, setIsPulling] = useState(false);
|
||||||
|
const [isPushing, setIsPushing] = useState(false);
|
||||||
const [isSwitching, setIsSwitching] = useState(false);
|
const [isSwitching, setIsSwitching] = useState(false);
|
||||||
const [worktrees, setWorktrees] = useState<WorktreeInfo[]>([]);
|
const [worktrees, setWorktrees] = useState<WorktreeInfo[]>([]);
|
||||||
const [branches, setBranches] = useState<BranchInfo[]>([]);
|
const [branches, setBranches] = useState<BranchInfo[]>([]);
|
||||||
|
const [aheadCount, setAheadCount] = useState(0);
|
||||||
|
const [behindCount, setBehindCount] = useState(0);
|
||||||
const [isLoadingBranches, setIsLoadingBranches] = useState(false);
|
const [isLoadingBranches, setIsLoadingBranches] = useState(false);
|
||||||
const [branchFilter, setBranchFilter] = useState("");
|
const [branchFilter, setBranchFilter] = useState("");
|
||||||
const currentWorktree = useAppStore((s) => s.getCurrentWorktree(projectPath));
|
const currentWorktree = useAppStore((s) => s.getCurrentWorktree(projectPath));
|
||||||
@@ -106,6 +110,8 @@ export function WorktreeSelector({
|
|||||||
const result = await api.worktree.listBranches(worktreePath);
|
const result = await api.worktree.listBranches(worktreePath);
|
||||||
if (result.success && result.result) {
|
if (result.success && result.result) {
|
||||||
setBranches(result.result.branches);
|
setBranches(result.result.branches);
|
||||||
|
setAheadCount(result.result.aheadCount || 0);
|
||||||
|
setBehindCount(result.result.behindCount || 0);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch branches:", error);
|
console.error("Failed to fetch branches:", error);
|
||||||
@@ -172,6 +178,32 @@ export function WorktreeSelector({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePush = async (worktree: WorktreeInfo) => {
|
||||||
|
if (isPushing) return;
|
||||||
|
setIsPushing(true);
|
||||||
|
try {
|
||||||
|
const api = getElectronAPI();
|
||||||
|
if (!api?.worktree?.push) {
|
||||||
|
toast.error("Push API not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await api.worktree.push(worktree.path);
|
||||||
|
if (result.success && result.result) {
|
||||||
|
toast.success(result.result.message);
|
||||||
|
// Refresh to update ahead/behind counts
|
||||||
|
fetchBranches(worktree.path);
|
||||||
|
fetchWorktrees();
|
||||||
|
} else {
|
||||||
|
toast.error(result.error || "Failed to push changes");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Push failed:", error);
|
||||||
|
toast.error("Failed to push changes");
|
||||||
|
} finally {
|
||||||
|
setIsPushing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const selectedWorktree =
|
const selectedWorktree =
|
||||||
worktrees.find((w) =>
|
worktrees.find((w) =>
|
||||||
currentWorktree ? w.path === currentWorktree : w.isMain
|
currentWorktree ? w.path === currentWorktree : w.isMain
|
||||||
@@ -273,6 +305,38 @@ export function WorktreeSelector({
|
|||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuLabel className="text-xs text-muted-foreground font-normal">
|
||||||
|
Sync
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
{/* Pull option */}
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handlePull(worktree)}
|
||||||
|
disabled={isPulling}
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
<Download className={cn("w-3.5 h-3.5 mr-2", isPulling && "animate-pulse")} />
|
||||||
|
{isPulling ? "Pulling..." : "Pull"}
|
||||||
|
{behindCount > 0 && (
|
||||||
|
<span className="ml-auto text-[10px] bg-muted px-1.5 py-0.5 rounded">
|
||||||
|
{behindCount} behind
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{/* Push option */}
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handlePush(worktree)}
|
||||||
|
disabled={isPushing || aheadCount === 0}
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
<Upload className={cn("w-3.5 h-3.5 mr-2", isPushing && "animate-pulse")} />
|
||||||
|
{isPushing ? "Pushing..." : "Push"}
|
||||||
|
{aheadCount > 0 && (
|
||||||
|
<span className="ml-auto text-[10px] bg-primary/20 text-primary px-1.5 py-0.5 rounded">
|
||||||
|
{aheadCount} ahead
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
{worktree.hasChanges && (
|
{worktree.hasChanges && (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => onCommit(worktree)}
|
onClick={() => onCommit(worktree)}
|
||||||
|
|||||||
@@ -1187,6 +1187,8 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
{ name: "develop", isCurrent: false, isRemote: false },
|
{ name: "develop", isCurrent: false, isRemote: false },
|
||||||
{ name: "feature/example", isCurrent: false, isRemote: false },
|
{ name: "feature/example", isCurrent: false, isRemote: false },
|
||||||
],
|
],
|
||||||
|
aheadCount: 2,
|
||||||
|
behindCount: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
2
apps/app/src/types/electron.d.ts
vendored
2
apps/app/src/types/electron.d.ts
vendored
@@ -765,6 +765,8 @@ export interface WorktreeAPI {
|
|||||||
isCurrent: boolean;
|
isCurrent: boolean;
|
||||||
isRemote: boolean;
|
isRemote: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
aheadCount: number;
|
||||||
|
behindCount: number;
|
||||||
};
|
};
|
||||||
error?: string;
|
error?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
@@ -53,11 +53,36 @@ export function createListBranchesHandler() {
|
|||||||
isRemote: false,
|
isRemote: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Get ahead/behind count for current branch
|
||||||
|
let aheadCount = 0;
|
||||||
|
let behindCount = 0;
|
||||||
|
try {
|
||||||
|
// First check if there's a remote tracking branch
|
||||||
|
const { stdout: upstreamOutput } = await execAsync(
|
||||||
|
`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`,
|
||||||
|
{ cwd: worktreePath }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (upstreamOutput.trim()) {
|
||||||
|
const { stdout: aheadBehindOutput } = await execAsync(
|
||||||
|
`git rev-list --left-right --count ${currentBranch}@{upstream}...HEAD`,
|
||||||
|
{ cwd: worktreePath }
|
||||||
|
);
|
||||||
|
const [behind, ahead] = aheadBehindOutput.trim().split(/\s+/).map(Number);
|
||||||
|
aheadCount = ahead || 0;
|
||||||
|
behindCount = behind || 0;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// No upstream branch set, that's okay
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
currentBranch,
|
currentBranch,
|
||||||
branches,
|
branches,
|
||||||
|
aheadCount,
|
||||||
|
behindCount,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user