feat: 新增分支管理、Stash贮藏、远程分支、设置页面\n\n分支管理:\n- 创建新分支\n- 删除本地分支(安全/强制)\n- 合并分支到当前分支\n\n远程分支:\n- 查看远程分支列表\n- 检出远程分支到本地\n- 删除远程分支\n\nStash 贮藏:\n- 保存当前变更(支持自定义消息)\n- 应用/弹出/删除贮藏\n- 贮藏列表管理\n\n设置:\n- Git 全局配置 (user.name / user.email)\n- 设置弹窗 UI"
This commit is contained in:
@@ -25,8 +25,14 @@ import {
|
|||||||
CloudUploadOutlined as PushTagIcon,
|
CloudUploadOutlined as PushTagIcon,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
SendOutlined,
|
SendOutlined,
|
||||||
|
MergeCellsOutlined,
|
||||||
|
InboxOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
GlobalOutlined,
|
||||||
|
SaveOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
} from '@ant-design/icons-vue'
|
} from '@ant-design/icons-vue'
|
||||||
import { Modal } from 'ant-design-vue'
|
import { Modal, message } from 'ant-design-vue'
|
||||||
import { AppService } from '../../bindings/github.com/zhuy1228/GitPilot/internal/app'
|
import { AppService } from '../../bindings/github.com/zhuy1228/GitPilot/internal/app'
|
||||||
import FileTreeNode from './FileTreeNode.vue'
|
import FileTreeNode from './FileTreeNode.vue'
|
||||||
import CommitFileTreeNode from './CommitFileTreeNode.vue'
|
import CommitFileTreeNode from './CommitFileTreeNode.vue'
|
||||||
@@ -76,6 +82,24 @@ const newTagName = ref('')
|
|||||||
const newTagMessage = ref('')
|
const newTagMessage = ref('')
|
||||||
const createTagLoading = ref(false)
|
const createTagLoading = ref(false)
|
||||||
|
|
||||||
|
// ---- 分支管理 ----
|
||||||
|
const newBranchName = ref('')
|
||||||
|
const createBranchLoading = ref(false)
|
||||||
|
const remoteBranches = ref([])
|
||||||
|
|
||||||
|
// ---- Stash 贮藏管理 ----
|
||||||
|
const stashList = ref([])
|
||||||
|
const stashLoading = ref(false)
|
||||||
|
const showStashSave = ref(false)
|
||||||
|
const stashMessage = ref('')
|
||||||
|
const stashSaveLoading = ref(false)
|
||||||
|
const showStashPanel = ref(false)
|
||||||
|
|
||||||
|
// ---- 设置 ----
|
||||||
|
const showSettings = ref(false)
|
||||||
|
const settingsLoading = ref(false)
|
||||||
|
const gitConfig = ref({ userName: '', userEmail: '' })
|
||||||
|
|
||||||
// ---- 文件列表拖拽调整宽度 ----
|
// ---- 文件列表拖拽调整宽度 ----
|
||||||
const fileListWidth = ref(300)
|
const fileListWidth = ref(300)
|
||||||
const MIN_FILELIST = 180
|
const MIN_FILELIST = 180
|
||||||
@@ -511,6 +535,8 @@ async function loadBranches() {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取分支失败:', e)
|
console.error('获取分支失败:', e)
|
||||||
}
|
}
|
||||||
|
loadRemoteBranches()
|
||||||
|
loadStashList()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function switchBranch(branchName) {
|
async function switchBranch(branchName) {
|
||||||
@@ -527,6 +553,247 @@ async function switchBranch(branchName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createBranch() {
|
||||||
|
if (!props.project?.path || !newBranchName.value.trim()) return
|
||||||
|
createBranchLoading.value = true
|
||||||
|
try {
|
||||||
|
await AppService.CreateBranch(props.project.path, newBranchName.value.trim())
|
||||||
|
newBranchName.value = ''
|
||||||
|
await loadBranches()
|
||||||
|
message.success('分支创建成功')
|
||||||
|
} catch (e) {
|
||||||
|
console.error('创建分支失败:', e)
|
||||||
|
Modal.error({ title: '创建分支失败', content: String(e) })
|
||||||
|
} finally {
|
||||||
|
createBranchLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteBranch(branchName, force = false) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除分支',
|
||||||
|
icon: h(ExclamationCircleOutlined),
|
||||||
|
content: h('div', [
|
||||||
|
h('p', `确定要删除分支吗?`),
|
||||||
|
h('p', { style: 'font-family: monospace; color: #89b4fa; font-size: 15px;' }, branchName),
|
||||||
|
]),
|
||||||
|
okText: '删除',
|
||||||
|
cancelText: '取消',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
await AppService.DeleteBranch(props.project.path, branchName, force)
|
||||||
|
await loadBranches()
|
||||||
|
message.success(`分支 ${branchName} 已删除`)
|
||||||
|
} catch (e) {
|
||||||
|
// 如果安全删除失败,提示是否强制删除
|
||||||
|
if (!force && String(e).includes('not fully merged')) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '分支未完全合并',
|
||||||
|
icon: h(ExclamationCircleOutlined),
|
||||||
|
content: h('div', [
|
||||||
|
h('p', `分支 ${branchName} 尚未完全合并到当前分支。`),
|
||||||
|
h('p', { style: 'color: #f38ba8;' }, '是否强制删除?'),
|
||||||
|
]),
|
||||||
|
okText: '强制删除',
|
||||||
|
cancelText: '取消',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
async onOk() {
|
||||||
|
await deleteBranch(branchName, true)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Modal.error({ title: '删除分支失败', content: String(e) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mergeBranch(branchName) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认合并分支',
|
||||||
|
icon: h(MergeCellsOutlined),
|
||||||
|
content: h('div', [
|
||||||
|
h('p', [
|
||||||
|
'将 ',
|
||||||
|
h('span', { style: 'font-family: monospace; color: #89b4fa;' }, branchName),
|
||||||
|
' 合并到 ',
|
||||||
|
h('span', { style: 'font-family: monospace; color: #a6e3a1;' }, status.value?.branch || '当前分支'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
okText: '合并',
|
||||||
|
cancelText: '取消',
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
const result = await AppService.MergeBranch(props.project.path, branchName)
|
||||||
|
message.success('合并成功')
|
||||||
|
await loadStatus()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('合并分支失败:', e)
|
||||||
|
Modal.error({ title: '合并失败', content: String(e) })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 远程分支 ----
|
||||||
|
async function loadRemoteBranches() {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
try {
|
||||||
|
const list = await AppService.GetRemoteBranches(props.project.path)
|
||||||
|
remoteBranches.value = Array.isArray(list) ? list : []
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取远程分支失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkoutRemoteBranch(remoteBranch) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
showBranchDropdown.value = false
|
||||||
|
try {
|
||||||
|
await AppService.CheckoutRemoteBranch(props.project.path, remoteBranch)
|
||||||
|
message.success('检出成功')
|
||||||
|
await loadStatus()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('检出远程分支失败:', e)
|
||||||
|
Modal.error({ title: '检出失败', content: String(e) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteRemoteBranch(remoteBranch) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除远程分支',
|
||||||
|
icon: h(ExclamationCircleOutlined),
|
||||||
|
content: h('div', [
|
||||||
|
h('p', '确定要删除远程分支吗?此操作不可逆!'),
|
||||||
|
h('p', { style: 'font-family: monospace; color: #f38ba8; font-size: 15px;' }, remoteBranch),
|
||||||
|
]),
|
||||||
|
okText: '删除',
|
||||||
|
cancelText: '取消',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
await AppService.DeleteRemoteBranch(props.project.path, remoteBranch)
|
||||||
|
await loadRemoteBranches()
|
||||||
|
message.success(`远程分支 ${remoteBranch} 已删除`)
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '删除远程分支失败', content: String(e) })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Stash 贮藏管理 ----
|
||||||
|
async function loadStashList() {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
stashLoading.value = true
|
||||||
|
try {
|
||||||
|
const list = await AppService.GetStashList(props.project.path)
|
||||||
|
stashList.value = Array.isArray(list) ? list : []
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取贮藏列表失败:', e)
|
||||||
|
stashList.value = []
|
||||||
|
} finally {
|
||||||
|
stashLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stashSave() {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
stashSaveLoading.value = true
|
||||||
|
try {
|
||||||
|
await AppService.StashSave(props.project.path, stashMessage.value.trim())
|
||||||
|
stashMessage.value = ''
|
||||||
|
showStashSave.value = false
|
||||||
|
message.success('已贮藏当前变更')
|
||||||
|
await refreshFiles()
|
||||||
|
await loadStashList()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('贮藏失败:', e)
|
||||||
|
Modal.error({ title: '贮藏失败', content: String(e) })
|
||||||
|
} finally {
|
||||||
|
stashSaveLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stashApply(index) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
try {
|
||||||
|
await AppService.StashApply(props.project.path, index)
|
||||||
|
message.success('已应用贮藏')
|
||||||
|
await refreshFiles()
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '应用贮藏失败', content: String(e) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stashPop(index) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
try {
|
||||||
|
await AppService.StashPop(props.project.path, index)
|
||||||
|
message.success('已应用并删除贮藏')
|
||||||
|
await refreshFiles()
|
||||||
|
await loadStashList()
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '应用贮藏失败', content: String(e) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stashDrop(index) {
|
||||||
|
if (!props.project?.path) return
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除贮藏',
|
||||||
|
icon: h(ExclamationCircleOutlined),
|
||||||
|
content: `确定要删除 stash@{${index}} 吗?`,
|
||||||
|
okText: '删除',
|
||||||
|
cancelText: '取消',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
await AppService.StashDrop(props.project.path, index)
|
||||||
|
message.success('已删除贮藏')
|
||||||
|
await loadStashList()
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '删除贮藏失败', content: String(e) })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 设置 ----
|
||||||
|
async function openSettings() {
|
||||||
|
showSettings.value = true
|
||||||
|
settingsLoading.value = true
|
||||||
|
try {
|
||||||
|
const config = await AppService.GetGitGlobalConfig()
|
||||||
|
gitConfig.value = {
|
||||||
|
userName: config?.userName || '',
|
||||||
|
userEmail: config?.userEmail || '',
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取 Git 配置失败:', e)
|
||||||
|
} finally {
|
||||||
|
settingsLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveSettings() {
|
||||||
|
settingsLoading.value = true
|
||||||
|
try {
|
||||||
|
await AppService.SetGitGlobalConfig(gitConfig.value.userName, gitConfig.value.userEmail)
|
||||||
|
message.success('设置已保存')
|
||||||
|
showSettings.value = false
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '保存设置失败', content: String(e) })
|
||||||
|
} finally {
|
||||||
|
settingsLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- 提交历史 ----
|
// ---- 提交历史 ----
|
||||||
async function loadCommitLog() {
|
async function loadCommitLog() {
|
||||||
if (!props.project?.path) return
|
if (!props.project?.path) return
|
||||||
@@ -783,23 +1050,76 @@ watch(activeTab, (tab) => {
|
|||||||
</a-tag>
|
</a-tag>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<div class="branch-dropdown">
|
<div class="branch-dropdown">
|
||||||
<div class="branch-dropdown-title">切换分支</div>
|
<!-- 创建新分支 -->
|
||||||
|
<div class="branch-create-box">
|
||||||
|
<a-input
|
||||||
|
v-model:value="newBranchName"
|
||||||
|
placeholder="新分支名称"
|
||||||
|
size="small"
|
||||||
|
style="flex:1"
|
||||||
|
@pressEnter="createBranch"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
:loading="createBranchLoading"
|
||||||
|
:disabled="!newBranchName.trim()"
|
||||||
|
@click="createBranch"
|
||||||
|
>
|
||||||
|
<template #icon><PlusOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<!-- 本地分支 -->
|
||||||
|
<div class="branch-dropdown-title">本地分支</div>
|
||||||
<div class="branch-dropdown-list">
|
<div class="branch-dropdown-list">
|
||||||
<div
|
<div
|
||||||
v-for="b in branches"
|
v-for="b in branches"
|
||||||
:key="b.name"
|
:key="b.name"
|
||||||
class="branch-dropdown-item"
|
class="branch-dropdown-item"
|
||||||
:class="{ active: b.current }"
|
:class="{ active: b.current }"
|
||||||
@click="switchBranch(b.name)"
|
|
||||||
>
|
>
|
||||||
|
<div class="branch-item-main" @click="switchBranch(b.name)">
|
||||||
<BranchesOutlined style="font-size: 12px; margin-right: 6px;" />
|
<BranchesOutlined style="font-size: 12px; margin-right: 6px;" />
|
||||||
{{ b.name }}
|
<span class="branch-item-name">{{ b.name }}</span>
|
||||||
<span v-if="b.current" style="margin-left: auto; color: var(--success, #a6e3a1);">✓</span>
|
<span v-if="b.current" style="margin-left: auto; color: var(--success, #a6e3a1);">✓</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!b.current" class="branch-item-actions" @click.stop>
|
||||||
|
<a-tooltip title="合并到当前分支">
|
||||||
|
<span class="branch-action-btn" @click="mergeBranch(b.name)"><MergeCellsOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="删除分支">
|
||||||
|
<span class="branch-action-btn danger" @click="deleteBranch(b.name)"><DeleteOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="!branches.length" style="padding: 12px; text-align: center; color: var(--text-muted);">
|
<div v-if="!branches.length" style="padding: 12px; text-align: center; color: var(--text-muted);">
|
||||||
无分支数据
|
无分支数据
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 远程分支 -->
|
||||||
|
<template v-if="remoteBranches.length">
|
||||||
|
<div class="branch-dropdown-title">远程分支</div>
|
||||||
|
<div class="branch-dropdown-list" style="max-height: 150px;">
|
||||||
|
<div
|
||||||
|
v-for="rb in remoteBranches"
|
||||||
|
:key="rb.name"
|
||||||
|
class="branch-dropdown-item remote"
|
||||||
|
>
|
||||||
|
<div class="branch-item-main" @click="checkoutRemoteBranch(rb.name)">
|
||||||
|
<GlobalOutlined style="font-size: 12px; margin-right: 6px; color: var(--text-muted);" />
|
||||||
|
<span class="branch-item-name">{{ rb.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="branch-item-actions" @click.stop>
|
||||||
|
<a-tooltip title="检出到本地">
|
||||||
|
<span class="branch-action-btn" @click="checkoutRemoteBranch(rb.name)"><DownloadOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="删除远程分支">
|
||||||
|
<span class="branch-action-btn danger" @click="deleteRemoteBranch(rb.name)"><DeleteOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
@@ -822,6 +1142,9 @@ watch(activeTab, (tab) => {
|
|||||||
<a-button size="small" :disabled="loadingBase" @click="loadStatus">
|
<a-button size="small" :disabled="loadingBase" @click="loadStatus">
|
||||||
<template #icon><ReloadOutlined /></template>
|
<template #icon><ReloadOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button size="small" @click="openSettings">
|
||||||
|
<template #icon><SettingOutlined /></template>
|
||||||
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -864,6 +1187,53 @@ watch(activeTab, (tab) => {
|
|||||||
>
|
>
|
||||||
Commit
|
Commit
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-button size="small" :disabled="!unstagedFiles.length && !stagedFiles.length" title="贮藏">
|
||||||
|
<template #icon><InboxOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<div class="stash-dropdown">
|
||||||
|
<div class="stash-save-section">
|
||||||
|
<a-input
|
||||||
|
v-model:value="stashMessage"
|
||||||
|
placeholder="贮藏描述 (可选)"
|
||||||
|
size="small"
|
||||||
|
style="flex:1"
|
||||||
|
@pressEnter="stashSave"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" size="small" :loading="stashSaveLoading" @click="stashSave">
|
||||||
|
<template #icon><SaveOutlined /></template>
|
||||||
|
Stash
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<div class="stash-list-title" v-if="stashList.length">
|
||||||
|
<InboxOutlined /> 贮藏列表 ({{ stashList.length }})
|
||||||
|
</div>
|
||||||
|
<div class="stash-list" v-if="stashList.length">
|
||||||
|
<div v-for="s in stashList" :key="s.index" class="stash-item">
|
||||||
|
<div class="stash-item-main">
|
||||||
|
<span class="stash-ref">{{ s.ref }}</span>
|
||||||
|
<span class="stash-msg">{{ s.message }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stash-item-actions">
|
||||||
|
<a-tooltip title="应用并删除">
|
||||||
|
<span class="stash-action-btn" @click="stashPop(s.index)"><CheckCircleOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="应用(保留)">
|
||||||
|
<span class="stash-action-btn" @click="stashApply(s.index)"><DownloadOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="删除">
|
||||||
|
<span class="stash-action-btn danger" @click="stashDrop(s.index)"><DeleteOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else style="padding: 12px; text-align: center; color: var(--text-muted); font-size: 12px;">
|
||||||
|
暂无贮藏
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1189,6 +1559,29 @@ watch(activeTab, (tab) => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 设置弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="showSettings"
|
||||||
|
title="设置"
|
||||||
|
:width="480"
|
||||||
|
@ok="saveSettings"
|
||||||
|
ok-text="保存"
|
||||||
|
cancel-text="取消"
|
||||||
|
:ok-button-props="{ loading: settingsLoading }"
|
||||||
|
>
|
||||||
|
<a-form layout="vertical" :style="{ marginTop: '16px' }">
|
||||||
|
<div style="margin-bottom: 16px; font-weight: 600; color: var(--text-secondary); font-size: 13px;">
|
||||||
|
<UserOutlined /> Git 全局配置
|
||||||
|
</div>
|
||||||
|
<a-form-item label="user.name">
|
||||||
|
<a-input v-model:value="gitConfig.userName" placeholder="Your Name" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="user.email">
|
||||||
|
<a-input v-model:value="gitConfig.userEmail" placeholder="you@example.com" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -1459,34 +1852,39 @@ watch(activeTab, (tab) => {
|
|||||||
background: var(--bg-surface, #252536);
|
background: var(--bg-surface, #252536);
|
||||||
border: 1px solid var(--border-color, #313244);
|
border: 1px solid var(--border-color, #313244);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
min-width: 200px;
|
min-width: 260px;
|
||||||
max-height: 300px;
|
max-height: 400px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.branch-dropdown-title {
|
.branch-create-box {
|
||||||
padding: 8px 12px;
|
display: flex;
|
||||||
font-size: 12px;
|
gap: 6px;
|
||||||
font-weight: 600;
|
padding: 8px 10px;
|
||||||
color: var(--text-secondary);
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-dropdown-title {
|
||||||
|
padding: 8px 12px 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-muted);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.3px;
|
letter-spacing: 0.3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.branch-dropdown-list {
|
.branch-dropdown-list {
|
||||||
max-height: 250px;
|
max-height: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.branch-dropdown-item {
|
.branch-dropdown-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 7px 12px;
|
padding: 0 4px 0 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.12s;
|
transition: background 0.12s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1499,6 +1897,159 @@ watch(activeTab, (tab) => {
|
|||||||
color: var(--success, #a6e3a1);
|
color: var(--success, #a6e3a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.branch-item-main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 7px 8px 7px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-item-name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-item-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-dropdown-item:hover .branch-item-actions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-action-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-action-btn:hover {
|
||||||
|
background: var(--bg-active, rgba(255,255,255,0.12));
|
||||||
|
color: var(--accent, #89b4fa);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-action-btn.danger:hover {
|
||||||
|
background: rgba(243, 139, 168, 0.2);
|
||||||
|
color: var(--danger, #f38ba8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stash 下拉 */
|
||||||
|
.stash-dropdown {
|
||||||
|
background: var(--bg-surface, #252536);
|
||||||
|
border: 1px solid var(--border-color, #313244);
|
||||||
|
border-radius: 6px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-height: 360px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-save-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-list-title {
|
||||||
|
padding: 8px 12px 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-list {
|
||||||
|
max-height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.04));
|
||||||
|
transition: background 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-item:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-item-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-ref {
|
||||||
|
font-family: 'Consolas', 'Courier New', monospace;
|
||||||
|
color: var(--accent, #89b4fa);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-msg {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-item-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-item:hover .stash-item-actions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-action-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-action-btn:hover {
|
||||||
|
background: var(--bg-active, rgba(255,255,255,0.12));
|
||||||
|
color: var(--accent, #89b4fa);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-action-btn.danger:hover {
|
||||||
|
background: rgba(243, 139, 168, 0.2);
|
||||||
|
color: var(--danger, #f38ba8);
|
||||||
|
}
|
||||||
|
|
||||||
/* Tab 切换 */
|
/* Tab 切换 */
|
||||||
.file-list-tabs {
|
.file-list-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export {
|
||||||
|
Settings
|
||||||
|
} from "./models.js";
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import { Create as $Create } from "@wailsio/runtime";
|
||||||
|
|
||||||
|
export class Settings {
|
||||||
|
/**
|
||||||
|
* Creates a new Settings instance.
|
||||||
|
* @param {Partial<Settings>} [$$source = {}] - The source object to create the Settings.
|
||||||
|
*/
|
||||||
|
constructor($$source = {}) {
|
||||||
|
if (!("Concurrency" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this["Concurrency"] = 0;
|
||||||
|
}
|
||||||
|
if (!("NetworkCheck" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this["NetworkCheck"] = false;
|
||||||
|
}
|
||||||
|
if (!("LogLevel" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["LogLevel"] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Settings instance from a string or object.
|
||||||
|
* @param {any} [$$source = {}]
|
||||||
|
* @returns {Settings}
|
||||||
|
*/
|
||||||
|
static createFrom($$source = {}) {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new Settings(/** @type {Partial<Settings>} */($$parsedSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,9 @@ import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Cr
|
|||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as application$0 from "../../../../wailsapp/wails/v3/pkg/application/models.js";
|
import * as application$0 from "../../../../wailsapp/wails/v3/pkg/application/models.js";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as config$0 from "../../config/models.js";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
@@ -52,6 +55,16 @@ export function AddUser(platform, username, token) {
|
|||||||
return $Call.ByID(2264426704, platform, username, token);
|
return $Call.ByID(2264426704, platform, username, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CheckoutRemoteBranch 检出远程分支到本地
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} remoteBranch
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function CheckoutRemoteBranch(path, remoteBranch) {
|
||||||
|
return $Call.ByID(3682237522, path, remoteBranch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CloneProject 克隆远程仓库到本地目录,并添加到项目树
|
* CloneProject 克隆远程仓库到本地目录,并添加到项目树
|
||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
@@ -75,6 +88,16 @@ export function CommitChanges(path, message) {
|
|||||||
return $Call.ByID(921984546, path, message);
|
return $Call.ByID(921984546, path, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateBranch 创建新分支
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function CreateBranch(path, name) {
|
||||||
|
return $Call.ByID(1422152924, path, name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateTag 创建标签
|
* CreateTag 创建标签
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -86,6 +109,27 @@ export function CreateTag(path, name, message) {
|
|||||||
return $Call.ByID(4209616956, path, name, message);
|
return $Call.ByID(4209616956, path, name, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteBranch 删除本地分支
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} name
|
||||||
|
* @param {boolean} force
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function DeleteBranch(path, name, force) {
|
||||||
|
return $Call.ByID(580272035, path, name, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteRemoteBranch 删除远程分支
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} branch
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function DeleteRemoteBranch(path, branch) {
|
||||||
|
return $Call.ByID(2626609049, path, branch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteTag 删除标签(本地+远程)
|
* DeleteTag 删除标签(本地+远程)
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -115,6 +159,16 @@ export function FetchProject(path) {
|
|||||||
return $Call.ByID(3541106829, path);
|
return $Call.ByID(3541106829, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetAppSettings 获取应用设置
|
||||||
|
* @returns {$CancellablePromise<config$0.Settings | null>}
|
||||||
|
*/
|
||||||
|
export function GetAppSettings() {
|
||||||
|
return $Call.ByID(428589026).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
|
return $$createType1($result);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetBranches 获取所有本地分支
|
* GetBranches 获取所有本地分支
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -122,7 +176,7 @@ export function FetchProject(path) {
|
|||||||
*/
|
*/
|
||||||
export function GetBranches(path) {
|
export function GetBranches(path) {
|
||||||
return $Call.ByID(1686190192, path).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(1686190192, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType1($result);
|
return $$createType3($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +209,7 @@ export function GetCommitFileDiff(path, hash, filePath) {
|
|||||||
*/
|
*/
|
||||||
export function GetCommitFiles(path, hash) {
|
export function GetCommitFiles(path, hash) {
|
||||||
return $Call.ByID(2420707876, path, hash).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(2420707876, path, hash).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType3($result);
|
return $$createType5($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +221,7 @@ export function GetCommitFiles(path, hash) {
|
|||||||
*/
|
*/
|
||||||
export function GetCommitLog(path, count) {
|
export function GetCommitLog(path, count) {
|
||||||
return $Call.ByID(1281870789, path, count).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(1281870789, path, count).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType5($result);
|
return $$createType7($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +255,16 @@ export function GetFileDiffStaged(projectPath, filePath) {
|
|||||||
return $Call.ByID(2773256455, projectPath, filePath);
|
return $Call.ByID(2773256455, projectPath, filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetGitGlobalConfig 获取 git 全局配置
|
||||||
|
* @returns {$CancellablePromise<$models.GitConfig | null>}
|
||||||
|
*/
|
||||||
|
export function GetGitGlobalConfig() {
|
||||||
|
return $Call.ByID(154811497).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
|
return $$createType9($result);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetPlatformInfo 获取平台信息
|
* GetPlatformInfo 获取平台信息
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
@@ -208,7 +272,7 @@ export function GetFileDiffStaged(projectPath, filePath) {
|
|||||||
*/
|
*/
|
||||||
export function GetPlatformInfo(name) {
|
export function GetPlatformInfo(name) {
|
||||||
return $Call.ByID(2668095547, name).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(2668095547, name).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType7($result);
|
return $$createType11($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +283,7 @@ export function GetPlatformInfo(name) {
|
|||||||
*/
|
*/
|
||||||
export function GetProjectChangedFiles(path) {
|
export function GetProjectChangedFiles(path) {
|
||||||
return $Call.ByID(2302591462, path).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(2302591462, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType9($result);
|
return $$createType13($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +294,7 @@ export function GetProjectChangedFiles(path) {
|
|||||||
*/
|
*/
|
||||||
export function GetProjectStatus(path) {
|
export function GetProjectStatus(path) {
|
||||||
return $Call.ByID(3451089027, path).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(3451089027, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType11($result);
|
return $$createType15($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +304,29 @@ export function GetProjectStatus(path) {
|
|||||||
*/
|
*/
|
||||||
export function GetProjectTree() {
|
export function GetProjectTree() {
|
||||||
return $Call.ByID(651189689).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(651189689).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType13($result);
|
return $$createType17($result);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetRemoteBranches 获取远程分支列表
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<$models.BranchInfo[]>}
|
||||||
|
*/
|
||||||
|
export function GetRemoteBranches(path) {
|
||||||
|
return $Call.ByID(994796370, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
|
return $$createType3($result);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetStashList 获取贮藏列表
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<$models.StashInfo[]>}
|
||||||
|
*/
|
||||||
|
export function GetStashList(path) {
|
||||||
|
return $Call.ByID(1948257945, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
|
return $$createType19($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +337,7 @@ export function GetProjectTree() {
|
|||||||
*/
|
*/
|
||||||
export function GetTags(path) {
|
export function GetTags(path) {
|
||||||
return $Call.ByID(3979169621, path).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(3979169621, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType15($result);
|
return $$createType21($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,10 +349,20 @@ export function GetTags(path) {
|
|||||||
*/
|
*/
|
||||||
export function GetUserInfo(platform, username) {
|
export function GetUserInfo(platform, username) {
|
||||||
return $Call.ByID(2674655707, platform, username).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(2674655707, platform, username).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType17($result);
|
return $$createType23($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MergeBranch 合并指定分支到当前分支
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} branch
|
||||||
|
* @returns {$CancellablePromise<string>}
|
||||||
|
*/
|
||||||
|
export function MergeBranch(path, branch) {
|
||||||
|
return $Call.ByID(278161076, path, branch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PullProject 拉取项目(当前分支)
|
* PullProject 拉取项目(当前分支)
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -363,6 +459,16 @@ export function SetApplication(app) {
|
|||||||
return $Call.ByID(383695022, app);
|
return $Call.ByID(383695022, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetGitGlobalConfig 设置 git 全局配置
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} email
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function SetGitGlobalConfig(name, email) {
|
||||||
|
return $Call.ByID(3875064981, name, email);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StageAll 暂存所有变更文件
|
* StageAll 暂存所有变更文件
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -382,6 +488,46 @@ export function StageFiles(path, files) {
|
|||||||
return $Call.ByID(4064506881, path, files);
|
return $Call.ByID(4064506881, path, files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StashApply 应用贮藏(不删除)
|
||||||
|
* @param {string} path
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function StashApply(path, index) {
|
||||||
|
return $Call.ByID(721583959, path, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StashDrop 删除贮藏
|
||||||
|
* @param {string} path
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function StashDrop(path, index) {
|
||||||
|
return $Call.ByID(3821556536, path, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StashPop 应用贮藏并删除
|
||||||
|
* @param {string} path
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function StashPop(path, index) {
|
||||||
|
return $Call.ByID(4117964460, path, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StashSave 保存当前变更到贮藏
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} message
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function StashSave(path, message) {
|
||||||
|
return $Call.ByID(1178263140, path, message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SwitchBranch 切换分支
|
* SwitchBranch 切换分支
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -411,6 +557,15 @@ export function UnstageFiles(path, files) {
|
|||||||
return $Call.ByID(3546473046, path, files);
|
return $Call.ByID(3546473046, path, files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateAppSettings 更新应用设置
|
||||||
|
* @param {string} logLevel
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function UpdateAppSettings(logLevel) {
|
||||||
|
return $Call.ByID(718820989, logLevel);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdatePlatform 修改平台信息(base_url)
|
* UpdatePlatform 修改平台信息(base_url)
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
@@ -434,21 +589,27 @@ export function UpdateUser(platform, oldUsername, newUsername, token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = $models.BranchInfo.createFrom;
|
const $$createType0 = config$0.Settings.createFrom;
|
||||||
const $$createType1 = $Create.Array($$createType0);
|
const $$createType1 = $Create.Nullable($$createType0);
|
||||||
const $$createType2 = $models.CommitFileInfo.createFrom;
|
const $$createType2 = $models.BranchInfo.createFrom;
|
||||||
const $$createType3 = $Create.Array($$createType2);
|
const $$createType3 = $Create.Array($$createType2);
|
||||||
const $$createType4 = $models.CommitLog.createFrom;
|
const $$createType4 = $models.CommitFileInfo.createFrom;
|
||||||
const $$createType5 = $Create.Array($$createType4);
|
const $$createType5 = $Create.Array($$createType4);
|
||||||
const $$createType6 = $models.PlatformInfo.createFrom;
|
const $$createType6 = $models.CommitLog.createFrom;
|
||||||
const $$createType7 = $Create.Nullable($$createType6);
|
const $$createType7 = $Create.Array($$createType6);
|
||||||
const $$createType8 = $models.FileInfo.createFrom;
|
const $$createType8 = $models.GitConfig.createFrom;
|
||||||
const $$createType9 = $Create.Array($$createType8);
|
const $$createType9 = $Create.Nullable($$createType8);
|
||||||
const $$createType10 = $models.ProjectStatus.createFrom;
|
const $$createType10 = $models.PlatformInfo.createFrom;
|
||||||
const $$createType11 = $Create.Nullable($$createType10);
|
const $$createType11 = $Create.Nullable($$createType10);
|
||||||
const $$createType12 = $models.TreeNode.createFrom;
|
const $$createType12 = $models.FileInfo.createFrom;
|
||||||
const $$createType13 = $Create.Array($$createType12);
|
const $$createType13 = $Create.Array($$createType12);
|
||||||
const $$createType14 = $models.TagInfo.createFrom;
|
const $$createType14 = $models.ProjectStatus.createFrom;
|
||||||
const $$createType15 = $Create.Array($$createType14);
|
const $$createType15 = $Create.Nullable($$createType14);
|
||||||
const $$createType16 = $models.UserInfo.createFrom;
|
const $$createType16 = $models.TreeNode.createFrom;
|
||||||
const $$createType17 = $Create.Nullable($$createType16);
|
const $$createType17 = $Create.Array($$createType16);
|
||||||
|
const $$createType18 = $models.StashInfo.createFrom;
|
||||||
|
const $$createType19 = $Create.Array($$createType18);
|
||||||
|
const $$createType20 = $models.TagInfo.createFrom;
|
||||||
|
const $$createType21 = $Create.Array($$createType20);
|
||||||
|
const $$createType22 = $models.UserInfo.createFrom;
|
||||||
|
const $$createType23 = $Create.Nullable($$createType22);
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ export {
|
|||||||
CommitFileInfo,
|
CommitFileInfo,
|
||||||
CommitLog,
|
CommitLog,
|
||||||
FileInfo,
|
FileInfo,
|
||||||
|
GitConfig,
|
||||||
PlatformInfo,
|
PlatformInfo,
|
||||||
ProjectStatus,
|
ProjectStatus,
|
||||||
|
StashInfo,
|
||||||
TagInfo,
|
TagInfo,
|
||||||
TreeNode,
|
TreeNode,
|
||||||
UserInfo
|
UserInfo
|
||||||
|
|||||||
@@ -207,6 +207,44 @@ export class FileInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitConfig Git 全局配置
|
||||||
|
*/
|
||||||
|
export class GitConfig {
|
||||||
|
/**
|
||||||
|
* Creates a new GitConfig instance.
|
||||||
|
* @param {Partial<GitConfig>} [$$source = {}] - The source object to create the GitConfig.
|
||||||
|
*/
|
||||||
|
constructor($$source = {}) {
|
||||||
|
if (!("userName" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["userName"] = "";
|
||||||
|
}
|
||||||
|
if (!("userEmail" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["userEmail"] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new GitConfig instance from a string or object.
|
||||||
|
* @param {any} [$$source = {}]
|
||||||
|
* @returns {GitConfig}
|
||||||
|
*/
|
||||||
|
static createFrom($$source = {}) {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new GitConfig(/** @type {Partial<GitConfig>} */($$parsedSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PlatformInfo 平台信息
|
* PlatformInfo 平台信息
|
||||||
*/
|
*/
|
||||||
@@ -294,6 +332,58 @@ export class ProjectStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StashInfo 贮藏信息
|
||||||
|
*/
|
||||||
|
export class StashInfo {
|
||||||
|
/**
|
||||||
|
* Creates a new StashInfo instance.
|
||||||
|
* @param {Partial<StashInfo>} [$$source = {}] - The source object to create the StashInfo.
|
||||||
|
*/
|
||||||
|
constructor($$source = {}) {
|
||||||
|
if (!("index" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this["index"] = 0;
|
||||||
|
}
|
||||||
|
if (!("ref" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["ref"] = "";
|
||||||
|
}
|
||||||
|
if (!("message" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["message"] = "";
|
||||||
|
}
|
||||||
|
if (!("timestamp" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this["timestamp"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new StashInfo instance from a string or object.
|
||||||
|
* @param {any} [$$source = {}]
|
||||||
|
* @returns {StashInfo}
|
||||||
|
*/
|
||||||
|
static createFrom($$source = {}) {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new StashInfo(/** @type {Partial<StashInfo>} */($$parsedSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TagInfo 标签信息
|
* TagInfo 标签信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -820,3 +820,232 @@ func (s *AppService) PushTag(path, name string) error {
|
|||||||
_, err := s.gitClient.PushTag(path, name)
|
_, err := s.gitClient.PushTag(path, name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 分支管理 ---
|
||||||
|
|
||||||
|
// CreateBranch 创建新分支
|
||||||
|
func (s *AppService) CreateBranch(path, name string) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("分支名不能为空")
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.CreateBranch(path, name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBranch 删除本地分支
|
||||||
|
func (s *AppService) DeleteBranch(path, name string, force bool) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("分支名不能为空")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if force {
|
||||||
|
_, err = s.gitClient.ForceDeleteBranch(path, name)
|
||||||
|
} else {
|
||||||
|
_, err = s.gitClient.DeleteBranch(path, name)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeBranch 合并指定分支到当前分支
|
||||||
|
func (s *AppService) MergeBranch(path, branch string) (string, error) {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return "", fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
branch = strings.TrimSpace(branch)
|
||||||
|
if branch == "" {
|
||||||
|
return "", fmt.Errorf("分支名不能为空")
|
||||||
|
}
|
||||||
|
return s.gitClient.MergeBranch(path, branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 远程分支管理 ---
|
||||||
|
|
||||||
|
// GetRemoteBranches 获取远程分支列表
|
||||||
|
func (s *AppService) GetRemoteBranches(path string) ([]BranchInfo, error) {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
out, err := s.gitClient.RemoteBranchList(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取远程分支列表失败: %w", err)
|
||||||
|
}
|
||||||
|
var branches []BranchInfo
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||||
|
name := strings.TrimSpace(line)
|
||||||
|
if name == "" || strings.Contains(name, "HEAD") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
branches = append(branches, BranchInfo{Name: name, Current: false})
|
||||||
|
}
|
||||||
|
return branches, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckoutRemoteBranch 检出远程分支到本地
|
||||||
|
func (s *AppService) CheckoutRemoteBranch(path, remoteBranch string) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
remoteBranch = strings.TrimSpace(remoteBranch)
|
||||||
|
if remoteBranch == "" {
|
||||||
|
return fmt.Errorf("远程分支名不能为空")
|
||||||
|
}
|
||||||
|
// origin/feature -> feature
|
||||||
|
localBranch := remoteBranch
|
||||||
|
if idx := strings.Index(remoteBranch, "/"); idx != -1 {
|
||||||
|
localBranch = remoteBranch[idx+1:]
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.CheckoutNewBranch(path, localBranch, remoteBranch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRemoteBranch 删除远程分支
|
||||||
|
func (s *AppService) DeleteRemoteBranch(path, branch string) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
branch = strings.TrimSpace(branch)
|
||||||
|
if branch == "" {
|
||||||
|
return fmt.Errorf("分支名不能为空")
|
||||||
|
}
|
||||||
|
// origin/feature -> feature
|
||||||
|
localName := branch
|
||||||
|
if idx := strings.Index(branch, "/"); idx != -1 {
|
||||||
|
localName = branch[idx+1:]
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.DeleteRemoteBranch(path, localName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Stash 贮藏管理 ---
|
||||||
|
|
||||||
|
// StashInfo 贮藏信息
|
||||||
|
type StashInfo struct {
|
||||||
|
Index int `json:"index"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashSave 保存当前变更到贮藏
|
||||||
|
func (s *AppService) StashSave(path, message string) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.StashSave(path, message)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStashList 获取贮藏列表
|
||||||
|
func (s *AppService) GetStashList(path string) ([]StashInfo, error) {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
out, err := s.gitClient.StashList(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取贮藏列表失败: %w", err)
|
||||||
|
}
|
||||||
|
var stashes []StashInfo
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, "\t", 3)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ref := parts[0]
|
||||||
|
message := parts[1]
|
||||||
|
var ts int64
|
||||||
|
if len(parts) >= 3 {
|
||||||
|
fmt.Sscanf(parts[2], "%d", &ts)
|
||||||
|
}
|
||||||
|
var index int
|
||||||
|
fmt.Sscanf(ref, "stash@{%d}", &index)
|
||||||
|
stashes = append(stashes, StashInfo{
|
||||||
|
Index: index,
|
||||||
|
Ref: ref,
|
||||||
|
Message: message,
|
||||||
|
Timestamp: ts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return stashes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashApply 应用贮藏(不删除)
|
||||||
|
func (s *AppService) StashApply(path string, index int) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.StashApply(path, index)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashPop 应用贮藏并删除
|
||||||
|
func (s *AppService) StashPop(path string, index int) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.StashPop(path, index)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashDrop 删除贮藏
|
||||||
|
func (s *AppService) StashDrop(path string, index int) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.StashDrop(path, index)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 设置管理 ---
|
||||||
|
|
||||||
|
// GitConfig Git 全局配置
|
||||||
|
type GitConfig struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
UserEmail string `json:"userEmail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGitGlobalConfig 获取 git 全局配置
|
||||||
|
func (s *AppService) GetGitGlobalConfig() (*GitConfig, error) {
|
||||||
|
name, _ := s.gitClient.GetGitGlobalConfig("user.name")
|
||||||
|
email, _ := s.gitClient.GetGitGlobalConfig("user.email")
|
||||||
|
return &GitConfig{
|
||||||
|
UserName: name,
|
||||||
|
UserEmail: email,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGitGlobalConfig 设置 git 全局配置
|
||||||
|
func (s *AppService) SetGitGlobalConfig(name, email string) error {
|
||||||
|
if name != "" {
|
||||||
|
if _, err := s.gitClient.SetGitGlobalConfig("user.name", name); err != nil {
|
||||||
|
return fmt.Errorf("设置 user.name 失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if email != "" {
|
||||||
|
if _, err := s.gitClient.SetGitGlobalConfig("user.email", email); err != nil {
|
||||||
|
return fmt.Errorf("设置 user.email 失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAppSettings 获取应用设置
|
||||||
|
func (s *AppService) GetAppSettings() *config.Settings {
|
||||||
|
return &s.config.Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAppSettings 更新应用设置
|
||||||
|
func (s *AppService) UpdateAppSettings(logLevel string) error {
|
||||||
|
s.config.Settings.LogLevel = logLevel
|
||||||
|
return config.SaveConfig(s.config)
|
||||||
|
}
|
||||||
|
|||||||
@@ -348,6 +348,101 @@ func parseNameStatus(output string) []FileChange {
|
|||||||
return changes
|
return changes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateBranch 创建新分支
|
||||||
|
func (g *GitClient) CreateBranch(path, name string) (string, error) {
|
||||||
|
return g.Run(path, "branch", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBranch 删除本地分支 (-d 安全删除)
|
||||||
|
func (g *GitClient) DeleteBranch(path, name string) (string, error) {
|
||||||
|
return g.Run(path, "branch", "-d", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceDeleteBranch 强制删除本地分支 (-D)
|
||||||
|
func (g *GitClient) ForceDeleteBranch(path, name string) (string, error) {
|
||||||
|
return g.Run(path, "branch", "-D", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeBranch 合并指定分支到当前分支
|
||||||
|
func (g *GitClient) MergeBranch(path, branch string) (string, error) {
|
||||||
|
return g.Run(path, "merge", branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRemoteBranch 删除远程分支
|
||||||
|
func (g *GitClient) DeleteRemoteBranch(path, branch string) (string, error) {
|
||||||
|
return g.Run(path, "push", "origin", "--delete", branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteBranchList 获取所有远程分支
|
||||||
|
func (g *GitClient) RemoteBranchList(path string) (string, error) {
|
||||||
|
return g.Run(path, "branch", "-r", "--format=%(refname:short)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckoutNewBranch 从远程分支检出新本地分支
|
||||||
|
func (g *GitClient) CheckoutNewBranch(path, localBranch, remoteBranch string) (string, error) {
|
||||||
|
return g.Run(path, "checkout", "-b", localBranch, remoteBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashSave 保存当前工作区变更到贮藏
|
||||||
|
func (g *GitClient) StashSave(path, message string) (string, error) {
|
||||||
|
if message != "" {
|
||||||
|
return g.Run(path, "stash", "push", "-m", message)
|
||||||
|
}
|
||||||
|
return g.Run(path, "stash", "push")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashList 获取贮藏列表
|
||||||
|
func (g *GitClient) StashList(path string) (string, error) {
|
||||||
|
return g.Run(path, "stash", "list", "--format=%gd\t%s\t%at")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashApply 应用贮藏(不删除)
|
||||||
|
func (g *GitClient) StashApply(path string, index int) (string, error) {
|
||||||
|
return g.Run(path, "stash", "apply", fmt.Sprintf("stash@{%d}", index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashPop 应用贮藏并删除
|
||||||
|
func (g *GitClient) StashPop(path string, index int) (string, error) {
|
||||||
|
return g.Run(path, "stash", "pop", fmt.Sprintf("stash@{%d}", index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StashDrop 删除贮藏
|
||||||
|
func (g *GitClient) StashDrop(path string, index int) (string, error) {
|
||||||
|
return g.Run(path, "stash", "drop", fmt.Sprintf("stash@{%d}", index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGitGlobalConfig 获取 git 全局配置
|
||||||
|
func (g *GitClient) GetGitGlobalConfig(key string) (string, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), g.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "git", "config", "--global", "--get", key)
|
||||||
|
hideWindow(cmd)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil // key 不存在不算错误
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(stdout.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGitGlobalConfig 设置 git 全局配置
|
||||||
|
func (g *GitClient) SetGitGlobalConfig(key, value string) (string, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), g.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "git", "config", "--global", key, value)
|
||||||
|
hideWindow(cmd)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("git config error: %v, stderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(stdout.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
// StatusText 返回变更状态的中文描述
|
// StatusText 返回变更状态的中文描述
|
||||||
func (fc FileChange) StatusText() string {
|
func (fc FileChange) StatusText() string {
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
Reference in New Issue
Block a user