新增功能区分推送与未推送的版本

This commit is contained in:
zyj
2026-03-09 16:55:44 +08:00
parent 1a44645a02
commit 29f1c0514a
7 changed files with 163 additions and 30 deletions

View File

@@ -1 +1 @@
c6a7af2915380768678ecf3032701af1 b3473eff2f1694108cc3ae68b4ae04f0

Binary file not shown.

View File

@@ -19,6 +19,7 @@ import {
UserOutlined, UserOutlined,
RollbackOutlined, RollbackOutlined,
ExclamationCircleOutlined, ExclamationCircleOutlined,
CloseCircleOutlined,
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { Modal } from 'ant-design-vue' import { Modal } from 'ant-design-vue'
import { AppService } from '../../bindings/github.com/zhuy1228/GitPilot/internal/app' import { AppService } from '../../bindings/github.com/zhuy1228/GitPilot/internal/app'
@@ -614,6 +615,33 @@ async function resetToCommit(commit, mode = 'hard') {
}) })
} }
async function revertCommit(commit) {
if (!props.project?.path || !commit) return
Modal.confirm({
title: '确认撤回提交',
icon: h(ExclamationCircleOutlined),
content: h('div', [
h('p', '将创建一个新提交来撤回以下提交的更改:'),
h('p', { style: 'font-family: monospace; color: #89b4fa;' }, `${commit.shortHash} - ${commit.message}`),
h('p', { style: 'color: var(--text-muted); font-size: 12px; margin-top: 8px;' }, '此操作会生成一个新的反向提交,不会丢失历史记录。'),
]),
okText: '确认撤回',
cancelText: '取消',
okButtonProps: { danger: true },
async onOk() {
try {
await AppService.RevertCommit(props.project.path, commit.hash)
await loadStatus()
selectedCommit.value = null
commitDiff.value = ''
} catch (e) {
console.error('撤回提交失败:', e)
Modal.error({ title: '撤回失败', content: String(e) })
}
},
})
}
function formatTime(ts) { function formatTime(ts) {
if (!ts) return '' if (!ts) return ''
const d = new Date(ts * 1000) const d = new Date(ts * 1000)
@@ -842,41 +870,56 @@ function formatTime(ts) {
v-for="log in commitLogs" v-for="log in commitLogs"
:key="log.hash" :key="log.hash"
class="commit-item" class="commit-item"
:class="{ active: selectedCommit?.hash === log.hash }" :class="{ active: selectedCommit?.hash === log.hash, unpushed: !log.pushed }"
@click="selectCommit(log)" @click="selectCommit(log)"
> >
<div class="commit-msg-row"> <div class="commit-msg-row">
<span v-if="!log.pushed" class="commit-unpushed-dot" title="未推送"></span>
<div class="commit-msg">{{ log.message }}</div> <div class="commit-msg">{{ log.message }}</div>
<a-dropdown :trigger="['click']" @click.stop> <div class="commit-action-btns" @click.stop>
<!-- 撤回按钮 -->
<a-button <a-button
type="text" type="text"
size="small" size="small"
class="commit-reset-btn" class="commit-action-btn"
:loading="resetLoading" title="撤回此提交"
@click.stop @click.stop="revertCommit(log)"
> >
<template #icon><RollbackOutlined /></template> <template #icon><CloseCircleOutlined /></template>
</a-button> </a-button>
<template #overlay> <!-- 回滚下拉 -->
<a-menu @click="({ key }) => resetToCommit(log, key)"> <a-dropdown :trigger="['click']">
<a-menu-item key="hard"> <a-button
<span style="color: #f38ba8;">🔴 硬回滚</span> type="text"
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">丢弃所有更改</span> size="small"
</a-menu-item> class="commit-action-btn"
<a-menu-item key="mixed"> :loading="resetLoading"
<span style="color: #fab387;">🟡 混合回滚</span> @click.stop
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">保留到工作区</span> >
</a-menu-item> <template #icon><RollbackOutlined /></template>
<a-menu-item key="soft"> </a-button>
<span style="color: #a6e3a1;">🟢 软回滚</span> <template #overlay>
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">保留到暂存区</span> <a-menu @click="({ key }) => resetToCommit(log, key)">
</a-menu-item> <a-menu-item key="hard">
</a-menu> <span style="color: #f38ba8;">🔴 硬回滚</span>
</template> <span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">丢弃所有更改</span>
</a-dropdown> </a-menu-item>
<a-menu-item key="mixed">
<span style="color: #fab387;">🟡 混合回滚</span>
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">保留到工作区</span>
</a-menu-item>
<a-menu-item key="soft">
<span style="color: #a6e3a1;">🟢 软回滚</span>
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">保留到暂存区</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div> </div>
<div class="commit-meta"> <div class="commit-meta">
<span class="commit-hash">{{ log.shortHash }}</span> <span class="commit-hash">{{ log.shortHash }}</span>
<span v-if="!log.pushed" class="commit-push-tag">未推送</span>
<span class="commit-author"><UserOutlined /> {{ log.author }}</span> <span class="commit-author"><UserOutlined /> {{ log.author }}</span>
<span class="commit-time">{{ formatTime(log.timestamp) }}</span> <span class="commit-time">{{ formatTime(log.timestamp) }}</span>
</div> </div>
@@ -1379,22 +1422,52 @@ function formatTime(ts) {
min-width: 0; min-width: 0;
} }
.commit-reset-btn { .commit-action-btns {
visibility: hidden; display: flex;
color: var(--text-muted) !important; align-items: center;
gap: 0;
flex-shrink: 0; flex-shrink: 0;
visibility: hidden;
}
.commit-action-btn {
color: var(--text-muted) !important;
width: 24px; width: 24px;
height: 24px; height: 24px;
} }
.commit-reset-btn:hover { .commit-action-btn:hover {
color: #f38ba8 !important; color: #f38ba8 !important;
} }
.commit-item:hover .commit-reset-btn { .commit-item:hover .commit-action-btns {
visibility: visible; visibility: visible;
} }
/* 未推送标识 */
.commit-unpushed-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #a6e3a1;
flex-shrink: 0;
margin-right: 2px;
}
.commit-push-tag {
font-size: 10px;
padding: 0 5px;
border-radius: 3px;
background: rgba(166, 227, 161, 0.15);
color: #a6e3a1;
line-height: 16px;
flex-shrink: 0;
}
.commit-item.unpushed {
border-left: 2px solid #a6e3a1;
}
.commit-msg { .commit-msg {
font-size: 13px; font-size: 13px;
color: var(--text-primary); color: var(--text-primary);

View File

@@ -282,6 +282,16 @@ export function ResetProject(path, hash, mode) {
return $Call.ByID(2061148684, path, hash, mode); return $Call.ByID(2061148684, path, hash, mode);
} }
/**
* RevertCommit 撤回指定提交(生成一个反向提交)
* @param {string} path
* @param {string} hash
* @returns {$CancellablePromise<void>}
*/
export function RevertCommit(path, hash) {
return $Call.ByID(1334788539, path, hash);
}
/** /**
* SelectDirectory 打开系统文件夹选择器,返回选中的路径 * SelectDirectory 打开系统文件夹选择器,返回选中的路径
* @returns {$CancellablePromise<string>} * @returns {$CancellablePromise<string>}

View File

@@ -133,6 +133,13 @@ export class CommitLog {
*/ */
this["message"] = ""; this["message"] = "";
} }
if (!("pushed" in $$source)) {
/**
* @member
* @type {boolean}
*/
this["pushed"] = false;
}
Object.assign(this, $$source); Object.assign(this, $$source);
} }

View File

@@ -569,6 +569,7 @@ type CommitLog struct {
Email string `json:"email"` Email string `json:"email"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
Message string `json:"message"` Message string `json:"message"`
Pushed bool `json:"pushed"`
} }
// BranchInfo 分支信息 // BranchInfo 分支信息
@@ -589,7 +590,39 @@ func (s *AppService) GetCommitLog(path string, count int) ([]CommitLog, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("获取提交历史失败: %w", err) return nil, fmt.Errorf("获取提交历史失败: %w", err)
} }
return parseCommitLog(out), nil logs := parseCommitLog(out)
// 获取当前分支名,查询未推送的提交
branch, brErr := s.gitClient.Branch(path)
if brErr == nil {
branch = strings.TrimSpace(branch)
unpushedOut, upErr := s.gitClient.UnpushedCommits(path, branch)
unpushedSet := make(map[string]bool)
if upErr == nil {
for _, h := range strings.Split(strings.TrimSpace(unpushedOut), "\n") {
if h != "" {
unpushedSet[h] = true
}
}
}
for i := range logs {
logs[i].Pushed = !unpushedSet[logs[i].Hash]
}
}
return logs, nil
}
// RevertCommit 撤回指定提交(生成一个反向提交)
func (s *AppService) RevertCommit(path, hash string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return fmt.Errorf("项目路径不存在: %s", path)
}
if strings.TrimSpace(hash) == "" {
return fmt.Errorf("提交哈希不能为空")
}
_, err := s.gitClient.RevertCommit(path, strings.TrimSpace(hash))
return err
} }
func parseCommitLog(output string) []CommitLog { func parseCommitLog(output string) []CommitLog {

View File

@@ -119,6 +119,16 @@ func (g *GitClient) Log(path string, count int) (string, error) {
) )
} }
// UnpushedCommits 获取当前分支上未推送到远程的提交哈希列表
func (g *GitClient) UnpushedCommits(path, branch string) (string, error) {
return g.Run(path, "rev-list", "origin/"+branch+"..HEAD")
}
// RevertCommit 撤回指定提交(创建一个反向提交)
func (g *GitClient) RevertCommit(path, hash string) (string, error) {
return g.Run(path, "revert", "--no-edit", hash)
}
// BranchList 获取所有本地分支 // BranchList 获取所有本地分支
func (g *GitClient) BranchList(path string) (string, error) { func (g *GitClient) BranchList(path string) (string, error) {
return g.Run(path, "branch", "--format=%(refname:short)\t%(HEAD)") return g.Run(path, "branch", "--format=%(refname:short)\t%(HEAD)")