添加git推送窗口,显示待提交列表和查看文件差异

This commit is contained in:
xintaofei
2026-03-21 20:37:19 +08:00
parent 048b8a8480
commit d9032f1c82
18 changed files with 986 additions and 137 deletions

View File

@@ -67,7 +67,6 @@ import {
gitInit,
gitPull,
gitFetch,
gitPush,
gitNewBranch,
gitWorktreeAdd,
gitCheckout,
@@ -78,9 +77,8 @@ import {
openFolderWindow,
openCommitWindow,
setFolderParentBranch,
gitListConflicts,
gitHasMergeHead,
openStashWindow,
openPushWindow,
} from "@/lib/tauri"
import { RemoteManageDialog } from "@/components/layout/remote-manage-dialog"
import { ConflictDialog } from "@/components/layout/conflict-dialog"
@@ -300,138 +298,6 @@ export function BranchDropdown({
})
}
// Uses operation "merge" intentionally: MERGE_HEAD exists so merge state is
// already active. MergeWorkspace won't call gitStartPullMerge (only for "pull"),
// and ConflictDialog abort correctly runs git merge --abort.
async function showMergeConflictDialog() {
try {
const remaining = await gitListConflicts(folderPath)
setConflictInfo({
has_conflicts: true,
conflicted_files: remaining,
operation: "merge",
})
} catch {
setConflictInfo({
has_conflicts: true,
conflicted_files: [],
operation: "merge",
})
}
}
async function handlePush() {
// Pre-check: if MERGE_HEAD exists, show conflict dialog immediately
try {
if (await gitHasMergeHead(folderPath)) {
await showMergeConflictDialog()
return
}
} catch {
// Pre-check failed, continue with normal push flow
}
const taskId = `git-${++taskSeq.current}-${Date.now()}`
const label = t("tasks.pushCode")
setLoading(true)
addTask(taskId, label)
updateTask(taskId, { status: "running" })
try {
const result = await withCredentialRetry(
(creds) => gitPush(folderPath, creds),
{ folderPath }
)
updateTask(taskId, { status: "completed" })
onBranchChange()
let description: string | undefined
if (result.upstream_set) {
description =
result.pushed_commits === 0
? t("toasts.upstreamSet")
: t("toasts.upstreamSetAndPushed", {
count: result.pushed_commits,
})
} else if (result.pushed_commits === 0) {
description = t("toasts.noCommitsToPush")
} else {
description = t("toasts.pushedCommits", {
count: result.pushed_commits,
})
}
toast.success(t("toasts.taskCompleted", { label }), {
description,
})
} catch (err) {
const errorMsg = toErrorMessage(err)
if (/MERGE_HEAD|unfinished merge/i.test(errorMsg)) {
// Unfinished merge — show conflict dialog
removeTask(taskId)
await showMergeConflictDialog()
} else if (/rejected|fetch first/i.test(errorMsg)) {
// Remote has new commits — auto-pull then retry push
updateTask(taskId, {
status: "running",
})
try {
const pullResult = await withCredentialRetry(
(creds) => gitPull(folderPath, creds),
{ folderPath }
)
if (pullResult.conflict?.has_conflicts) {
removeTask(taskId)
onBranchChange()
setConflictInfo(pullResult.conflict)
} else {
// Pull succeeded, retry push
updateTask(taskId, { status: "running" })
const pushResult = await withCredentialRetry(
(creds) => gitPush(folderPath, creds),
{ folderPath }
)
updateTask(taskId, { status: "completed" })
onBranchChange()
let description: string | undefined
if (pushResult.upstream_set) {
description =
pushResult.pushed_commits === 0
? t("toasts.upstreamSet")
: t("toasts.upstreamSetAndPushed", {
count: pushResult.pushed_commits,
})
} else if (pushResult.pushed_commits === 0) {
description = t("toasts.noCommitsToPush")
} else {
description = t("toasts.pushedCommits", {
count: pushResult.pushed_commits,
})
}
toast.success(t("toasts.taskCompleted", { label }), {
description,
})
}
} catch (pullErr) {
const pullErrMsg = toErrorMessage(pullErr)
if (/MERGE_HEAD|unfinished merge/i.test(pullErrMsg)) {
removeTask(taskId)
await showMergeConflictDialog()
} else {
removeTask(taskId)
const pullErrTitle = t("toasts.taskFailed", { label })
pushAlert("error", pullErrTitle, pullErrMsg)
toast.error(pullErrTitle, { description: pullErrMsg })
}
}
} else {
removeTask(taskId)
const errorTitle = t("toasts.taskFailed", { label })
pushAlert("error", errorTitle, errorMsg)
toast.error(errorTitle, { description: errorMsg })
}
} finally {
setLoading(false)
}
}
function handleMergeParent() {
if (!parentBranch) return
setConfirmAction({ type: "merge", branchName: parentBranch })
@@ -751,7 +617,19 @@ export function BranchDropdown({
<GitCommitHorizontal className="h-3.5 w-3.5" />
{t("openCommitWindow")}
</DropdownMenuItem>
<DropdownMenuItem disabled={loading} onSelect={handlePush}>
<DropdownMenuItem
disabled={loading}
onSelect={() => {
if (!folder) return
setDropdownOpen(false)
openPushWindow(folder.id).catch((err) => {
const title = t("toasts.openPushWindowFailed")
const msg = toErrorMessage(err)
pushAlert("error", title, msg)
toast.error(title, { description: msg })
})
}}
>
<Upload className="h-3.5 w-3.5" />
{t("pushCode")}
</DropdownMenuItem>