Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ec453cc38 |
@@ -9,7 +9,7 @@ info:
|
|||||||
description: "Git Repository Management Tool"
|
description: "Git Repository Management Tool"
|
||||||
copyright: "(c) 2025, GitPilot"
|
copyright: "(c) 2025, GitPilot"
|
||||||
comments: "GitPilot - Git Repository Manager"
|
comments: "GitPilot - Git Repository Manager"
|
||||||
version: "0.3.0"
|
version: "0.3.1"
|
||||||
|
|
||||||
dev_mode:
|
dev_mode:
|
||||||
root_path: .
|
root_path: .
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.gitpilot.app</string>
|
<string>com.gitpilot.app</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>0.3.0</string>
|
<string>0.3.1</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>Git Repository Management Tool</string>
|
<string>Git Repository Management Tool</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.3.0</string>
|
<string>0.3.1</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>icons</string>
|
<string>icons</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"fixed": {
|
"fixed": {
|
||||||
"file_version": "0.3.0"
|
"file_version": "0.3.1"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"0000": {
|
"0000": {
|
||||||
"ProductVersion": "0.3.0",
|
"ProductVersion": "0.3.1",
|
||||||
"CompanyName": "GitPilot",
|
"CompanyName": "GitPilot",
|
||||||
"FileDescription": "Git Repository Management Tool",
|
"FileDescription": "Git Repository Management Tool",
|
||||||
"LegalCopyright": "© 2025, GitPilot",
|
"LegalCopyright": "© 2025, GitPilot",
|
||||||
|
|||||||
@@ -90,6 +90,15 @@ const newBranchName = ref('')
|
|||||||
const createBranchLoading = ref(false)
|
const createBranchLoading = ref(false)
|
||||||
const remoteBranches = ref([])
|
const remoteBranches = ref([])
|
||||||
|
|
||||||
|
// ---- Remote 远程仓库 ----
|
||||||
|
const remotes = ref([])
|
||||||
|
const currentRemote = ref('origin')
|
||||||
|
const showRemoteDropdown = ref(false)
|
||||||
|
const showAddRemote = ref(false)
|
||||||
|
const newRemoteName = ref('')
|
||||||
|
const newRemoteUrl = ref('')
|
||||||
|
const addRemoteLoading = ref(false)
|
||||||
|
|
||||||
// ---- Stash 贮藏管理 ----
|
// ---- Stash 贮藏管理 ----
|
||||||
const stashList = ref([])
|
const stashList = ref([])
|
||||||
const stashLoading = ref(false)
|
const stashLoading = ref(false)
|
||||||
@@ -248,8 +257,15 @@ async function loadStatus() {
|
|||||||
status.value = {
|
status.value = {
|
||||||
branch: result.branch || '',
|
branch: result.branch || '',
|
||||||
remoteUrl: result.remoteUrl || result.remoteURL || '',
|
remoteUrl: result.remoteUrl || result.remoteURL || '',
|
||||||
|
remotes: Array.isArray(result.remotes) ? result.remotes : [],
|
||||||
|
currentRemote: result.currentRemote || 'origin',
|
||||||
changedFiles: [],
|
changedFiles: [],
|
||||||
}
|
}
|
||||||
|
// 同步 remotes
|
||||||
|
remotes.value = status.value.remotes
|
||||||
|
if (remotes.value.length && !remotes.value.find(r => r.name === currentRemote.value)) {
|
||||||
|
currentRemote.value = remotes.value[0].name
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorMsg.value = '获取项目状态返回空值: ' + JSON.stringify(result)
|
errorMsg.value = '获取项目状态返回空值: ' + JSON.stringify(result)
|
||||||
loadingBase.value = false
|
loadingBase.value = false
|
||||||
@@ -512,11 +528,11 @@ async function gitAction(action) {
|
|||||||
actionLoading.value = action
|
actionLoading.value = action
|
||||||
try {
|
try {
|
||||||
if (action === 'pull') {
|
if (action === 'pull') {
|
||||||
await AppService.PullProject(props.project.path)
|
await AppService.PullProject(props.project.path, currentRemote.value)
|
||||||
} else if (action === 'push') {
|
} else if (action === 'push') {
|
||||||
await AppService.PushProject(props.project.path)
|
await AppService.PushProject(props.project.path, currentRemote.value)
|
||||||
} else {
|
} else {
|
||||||
await AppService.FetchProject(props.project.path)
|
await AppService.FetchProject(props.project.path, currentRemote.value)
|
||||||
}
|
}
|
||||||
await loadStatus()
|
await loadStatus()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -675,7 +691,7 @@ async function mergeBranch(branchName) {
|
|||||||
async function loadRemoteBranches() {
|
async function loadRemoteBranches() {
|
||||||
if (!props.project?.path) return
|
if (!props.project?.path) return
|
||||||
try {
|
try {
|
||||||
const list = await AppService.GetRemoteBranches(props.project.path)
|
const list = await AppService.GetRemoteBranches(props.project.path, currentRemote.value)
|
||||||
remoteBranches.value = Array.isArray(list) ? list : []
|
remoteBranches.value = Array.isArray(list) ? list : []
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取远程分支失败:', e)
|
console.error('获取远程分支失败:', e)
|
||||||
@@ -709,7 +725,7 @@ async function deleteRemoteBranch(remoteBranch) {
|
|||||||
okButtonProps: { danger: true },
|
okButtonProps: { danger: true },
|
||||||
async onOk() {
|
async onOk() {
|
||||||
try {
|
try {
|
||||||
await AppService.DeleteRemoteBranch(props.project.path, remoteBranch)
|
await AppService.DeleteRemoteBranch(props.project.path, remoteBranch, currentRemote.value)
|
||||||
await loadRemoteBranches()
|
await loadRemoteBranches()
|
||||||
message.success(`远程分支 ${remoteBranch} 已删除`)
|
message.success(`远程分支 ${remoteBranch} 已删除`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -1130,7 +1146,7 @@ async function deleteTag(tag) {
|
|||||||
okButtonProps: { danger: true },
|
okButtonProps: { danger: true },
|
||||||
async onOk() {
|
async onOk() {
|
||||||
try {
|
try {
|
||||||
await AppService.DeleteTag(props.project.path, tag.name)
|
await AppService.DeleteTag(props.project.path, tag.name, currentRemote.value)
|
||||||
await loadTags()
|
await loadTags()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('删除标签失败:', e)
|
console.error('删除标签失败:', e)
|
||||||
@@ -1143,7 +1159,7 @@ async function deleteTag(tag) {
|
|||||||
async function pushTag(tag) {
|
async function pushTag(tag) {
|
||||||
if (!props.project?.path) return
|
if (!props.project?.path) return
|
||||||
try {
|
try {
|
||||||
await AppService.PushTag(props.project.path, tag.name)
|
await AppService.PushTag(props.project.path, tag.name, currentRemote.value)
|
||||||
Modal.success({ title: '推送成功', content: `标签 ${tag.name} 已推送到远程` })
|
Modal.success({ title: '推送成功', content: `标签 ${tag.name} 已推送到远程` })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('推送标签失败:', e)
|
console.error('推送标签失败:', e)
|
||||||
@@ -1157,6 +1173,63 @@ watch(activeTab, (tab) => {
|
|||||||
loadTags()
|
loadTags()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ---- Remote 远程仓库管理 ----
|
||||||
|
async function switchRemote(remoteName) {
|
||||||
|
currentRemote.value = remoteName
|
||||||
|
showRemoteDropdown.value = false
|
||||||
|
// 切换 remote 后刷新远程分支
|
||||||
|
await loadRemoteBranches()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addRemote() {
|
||||||
|
if (!props.project?.path || !newRemoteName.value.trim() || !newRemoteUrl.value.trim()) return
|
||||||
|
addRemoteLoading.value = true
|
||||||
|
try {
|
||||||
|
await AppService.AddRemote(props.project.path, newRemoteName.value.trim(), newRemoteUrl.value.trim())
|
||||||
|
newRemoteName.value = ''
|
||||||
|
newRemoteUrl.value = ''
|
||||||
|
showAddRemote.value = false
|
||||||
|
message.success('远程仓库添加成功')
|
||||||
|
await loadStatus()
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '添加远程仓库失败', content: String(e) })
|
||||||
|
} finally {
|
||||||
|
addRemoteLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeRemote(remoteName) {
|
||||||
|
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;' }, remoteName),
|
||||||
|
]),
|
||||||
|
okText: '删除',
|
||||||
|
cancelText: '取消',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
await AppService.RemoveRemote(props.project.path, remoteName)
|
||||||
|
message.success(`远程仓库 ${remoteName} 已删除`)
|
||||||
|
if (currentRemote.value === remoteName) {
|
||||||
|
currentRemote.value = 'origin'
|
||||||
|
}
|
||||||
|
await loadStatus()
|
||||||
|
} catch (e) {
|
||||||
|
Modal.error({ title: '删除远程仓库失败', content: String(e) })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentRemoteUrl = computed(() => {
|
||||||
|
const r = remotes.value.find(r => r.name === currentRemote.value)
|
||||||
|
return r ? r.url : status.value?.remoteUrl || ''
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1271,7 +1344,58 @@ watch(activeTab, (tab) => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
<span v-if="status.remoteUrl" class="remote-url" :title="status.remoteUrl">{{ status.remoteUrl }}</span>
|
<span v-if="status.remoteUrl" class="remote-url" :title="currentRemoteUrl">
|
||||||
|
<a-dropdown :open="showRemoteDropdown" @openChange="v => showRemoteDropdown = v" :trigger="['click']">
|
||||||
|
<span class="remote-selector" @click.prevent="showRemoteDropdown = !showRemoteDropdown">
|
||||||
|
<GlobalOutlined style="margin-right: 4px;" />
|
||||||
|
{{ currentRemote }}
|
||||||
|
<CaretDownOutlined style="font-size: 10px; margin-left: 2px;" />
|
||||||
|
</span>
|
||||||
|
<template #overlay>
|
||||||
|
<div class="remote-dropdown">
|
||||||
|
<div class="branch-dropdown-title">远程仓库</div>
|
||||||
|
<div class="branch-dropdown-list">
|
||||||
|
<div
|
||||||
|
v-for="r in remotes"
|
||||||
|
:key="r.name"
|
||||||
|
class="branch-dropdown-item"
|
||||||
|
:class="{ active: r.name === currentRemote }"
|
||||||
|
>
|
||||||
|
<div class="branch-item-main" @click="switchRemote(r.name)">
|
||||||
|
<GlobalOutlined style="font-size: 12px; margin-right: 6px;" />
|
||||||
|
<span class="branch-item-name">{{ r.name }}</span>
|
||||||
|
<span v-if="r.name === currentRemote" style="margin-left: auto; color: var(--success, #a6e3a1);">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="branch-item-actions" @click.stop>
|
||||||
|
<a-tooltip :title="r.url">
|
||||||
|
<span class="branch-action-btn" style="cursor: default; opacity: 0.6; font-size: 10px;">URL</span>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="删除远程仓库" v-if="r.name !== 'origin'">
|
||||||
|
<span class="branch-action-btn danger" @click="removeRemote(r.name)"><DeleteOutlined /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!remotes.length" style="padding: 12px; text-align: center; color: var(--text-muted);">
|
||||||
|
无远程仓库
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 添加远程仓库 -->
|
||||||
|
<div v-if="!showAddRemote" class="remote-add-btn" @click="showAddRemote = true">
|
||||||
|
<PlusOutlined /> 添加远程仓库
|
||||||
|
</div>
|
||||||
|
<div v-else class="remote-add-form">
|
||||||
|
<a-input v-model:value="newRemoteName" placeholder="名称 (如 upstream)" size="small" style="margin-bottom: 4px;" />
|
||||||
|
<a-input v-model:value="newRemoteUrl" placeholder="仓库地址" size="small" style="margin-bottom: 4px;" />
|
||||||
|
<div style="display: flex; gap: 4px;">
|
||||||
|
<a-button size="small" type="primary" :loading="addRemoteLoading" :disabled="!newRemoteName.trim() || !newRemoteUrl.trim()" @click="addRemote" style="flex: 1;">添加</a-button>
|
||||||
|
<a-button size="small" @click="showAddRemote = false; newRemoteName = ''; newRemoteUrl = ''">取消</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
<span class="remote-url-text" :title="currentRemoteUrl">{{ currentRemoteUrl }}</span>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<a-space class="project-actions">
|
<a-space class="project-actions">
|
||||||
@@ -1898,6 +2022,56 @@ watch(activeTab, (tab) => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remote-selector {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--accent, #89b4fa);
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
.remote-selector:hover {
|
||||||
|
background: var(--hover-bg, rgba(137, 180, 250, 0.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.remote-url-text {
|
||||||
|
max-width: 220px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remote-dropdown {
|
||||||
|
background: var(--dropdown-bg, #1e1e2e);
|
||||||
|
border: 1px solid var(--border-color, #313244);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 0;
|
||||||
|
min-width: 280px;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
.remote-add-btn {
|
||||||
|
padding: 8px 12px;
|
||||||
|
color: var(--accent, #89b4fa);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
border-top: 1px solid var(--border-color, #313244);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.remote-add-btn:hover {
|
||||||
|
background: var(--hover-bg, rgba(137, 180, 250, 0.08));
|
||||||
|
}
|
||||||
|
.remote-add-form {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-top: 1px solid var(--border-color, #313244);
|
||||||
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-actions {
|
.project-actions {
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ async function doBatchPull() {
|
|||||||
if (!selectedBatchPaths.value.length) return
|
if (!selectedBatchPaths.value.length) return
|
||||||
batchActionLoading.value = 'pull'
|
batchActionLoading.value = 'pull'
|
||||||
try {
|
try {
|
||||||
const results = await AppService.BatchPull(selectedBatchPaths.value)
|
const results = await AppService.BatchPull(selectedBatchPaths.value, 'origin')
|
||||||
batchResults.value = Array.isArray(results) ? results : []
|
batchResults.value = Array.isArray(results) ? results : []
|
||||||
const successCount = batchResults.value.filter(r => r.success).length
|
const successCount = batchResults.value.filter(r => r.success).length
|
||||||
message.info(`批量 Pull 完成:${successCount}/${batchResults.value.length} 成功`)
|
message.info(`批量 Pull 完成:${successCount}/${batchResults.value.length} 成功`)
|
||||||
@@ -473,7 +473,7 @@ async function doBatchPush() {
|
|||||||
if (!selectedBatchPaths.value.length) return
|
if (!selectedBatchPaths.value.length) return
|
||||||
batchActionLoading.value = 'push'
|
batchActionLoading.value = 'push'
|
||||||
try {
|
try {
|
||||||
const results = await AppService.BatchPush(selectedBatchPaths.value)
|
const results = await AppService.BatchPush(selectedBatchPaths.value, 'origin')
|
||||||
batchResults.value = Array.isArray(results) ? results : []
|
batchResults.value = Array.isArray(results) ? results : []
|
||||||
const successCount = batchResults.value.filter(r => r.success).length
|
const successCount = batchResults.value.filter(r => r.success).length
|
||||||
message.info(`批量 Push 完成:${successCount}/${batchResults.value.length} 成功`)
|
message.info(`批量 Push 完成:${successCount}/${batchResults.value.length} 成功`)
|
||||||
|
|||||||
@@ -53,6 +53,17 @@ export function AddProject(platform, username, name, path) {
|
|||||||
return $Call.ByID(2299402672, platform, username, name, path);
|
return $Call.ByID(2299402672, platform, username, name, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AddRemote 添加远程仓库
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function AddRemote(path, name, url) {
|
||||||
|
return $Call.ByID(3697916441, path, name, url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AddUser 添加用户到指定平台
|
* AddUser 添加用户到指定平台
|
||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
@@ -67,10 +78,11 @@ export function AddUser(platform, username, token) {
|
|||||||
/**
|
/**
|
||||||
* BatchPull 批量拉取指定项目
|
* BatchPull 批量拉取指定项目
|
||||||
* @param {string[]} paths
|
* @param {string[]} paths
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<$models.BatchPullResult[]>}
|
* @returns {$CancellablePromise<$models.BatchPullResult[]>}
|
||||||
*/
|
*/
|
||||||
export function BatchPull(paths) {
|
export function BatchPull(paths, remote) {
|
||||||
return $Call.ByID(758996647, paths).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(758996647, paths, remote).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -78,10 +90,11 @@ export function BatchPull(paths) {
|
|||||||
/**
|
/**
|
||||||
* BatchPush 批量推送指定项目
|
* BatchPush 批量推送指定项目
|
||||||
* @param {string[]} paths
|
* @param {string[]} paths
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<$models.BatchPullResult[]>}
|
* @returns {$CancellablePromise<$models.BatchPullResult[]>}
|
||||||
*/
|
*/
|
||||||
export function BatchPush(paths) {
|
export function BatchPush(paths, remote) {
|
||||||
return $Call.ByID(794082076, paths).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(794082076, paths, remote).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -165,20 +178,22 @@ export function DeleteBranch(path, name, force) {
|
|||||||
* DeleteRemoteBranch 删除远程分支
|
* DeleteRemoteBranch 删除远程分支
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @param {string} branch
|
* @param {string} branch
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<void>}
|
* @returns {$CancellablePromise<void>}
|
||||||
*/
|
*/
|
||||||
export function DeleteRemoteBranch(path, branch) {
|
export function DeleteRemoteBranch(path, branch, remote) {
|
||||||
return $Call.ByID(2626609049, path, branch);
|
return $Call.ByID(2626609049, path, branch, remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteTag 删除标签(本地+远程)
|
* DeleteTag 删除标签(本地+远程)
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<void>}
|
* @returns {$CancellablePromise<void>}
|
||||||
*/
|
*/
|
||||||
export function DeleteTag(path, name) {
|
export function DeleteTag(path, name, remote) {
|
||||||
return $Call.ByID(873653241, path, name);
|
return $Call.ByID(873653241, path, name, remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,12 +207,13 @@ export function DiscardFiles(path, files) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FetchProject 拉取远程信息
|
* FetchProject 拉取远程信息(指定 remote,空则 fetch --all)
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<string>}
|
* @returns {$CancellablePromise<string>}
|
||||||
*/
|
*/
|
||||||
export function FetchProject(path) {
|
export function FetchProject(path, remote) {
|
||||||
return $Call.ByID(3541106829, path);
|
return $Call.ByID(3541106829, path, remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -383,14 +399,26 @@ export function GetProjectTree() {
|
|||||||
/**
|
/**
|
||||||
* GetRemoteBranches 获取远程分支列表
|
* GetRemoteBranches 获取远程分支列表
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<$models.BranchInfo[]>}
|
* @returns {$CancellablePromise<$models.BranchInfo[]>}
|
||||||
*/
|
*/
|
||||||
export function GetRemoteBranches(path) {
|
export function GetRemoteBranches(path, remote) {
|
||||||
return $Call.ByID(994796370, path).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(994796370, path, remote).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType8($result);
|
return $$createType8($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetRemotes 获取项目所有远程仓库列表
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<$models.RemoteItem[]>}
|
||||||
|
*/
|
||||||
|
export function GetRemotes(path) {
|
||||||
|
return $Call.ByID(975520027, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
|
return $$createType26($result);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetStashList 获取贮藏列表
|
* GetStashList 获取贮藏列表
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
@@ -398,7 +426,7 @@ export function GetRemoteBranches(path) {
|
|||||||
*/
|
*/
|
||||||
export function GetStashList(path) {
|
export function GetStashList(path) {
|
||||||
return $Call.ByID(1948257945, path).then(/** @type {($result: any) => any} */(($result) => {
|
return $Call.ByID(1948257945, path).then(/** @type {($result: any) => any} */(($result) => {
|
||||||
return $$createType26($result);
|
return $$createType28($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +437,7 @@ export function GetStashList(path) {
|
|||||||
*/
|
*/
|
||||||
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 $$createType28($result);
|
return $$createType30($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +449,7 @@ 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 $$createType30($result);
|
return $$createType32($result);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,31 +483,34 @@ export function MergeBranch(path, branch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PullProject 拉取项目(当前分支)
|
* PullProject 拉取项目(当前分支,指定 remote)
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<string>}
|
* @returns {$CancellablePromise<string>}
|
||||||
*/
|
*/
|
||||||
export function PullProject(path) {
|
export function PullProject(path, remote) {
|
||||||
return $Call.ByID(4145813996, path);
|
return $Call.ByID(4145813996, path, remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PushProject 推送项目(当前分支)
|
* PushProject 推送项目(当前分支,指定 remote)
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<string>}
|
* @returns {$CancellablePromise<string>}
|
||||||
*/
|
*/
|
||||||
export function PushProject(path) {
|
export function PushProject(path, remote) {
|
||||||
return $Call.ByID(3887901113, path);
|
return $Call.ByID(3887901113, path, remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PushTag 推送标签到远程
|
* PushTag 推送标签到远程
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
|
* @param {string} remote
|
||||||
* @returns {$CancellablePromise<void>}
|
* @returns {$CancellablePromise<void>}
|
||||||
*/
|
*/
|
||||||
export function PushTag(path, name) {
|
export function PushTag(path, name, remote) {
|
||||||
return $Call.ByID(1424810524, path, name);
|
return $Call.ByID(1424810524, path, name, remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -502,6 +533,16 @@ export function RemoveProject(platform, username, name) {
|
|||||||
return $Call.ByID(2748109581, platform, username, name);
|
return $Call.ByID(2748109581, platform, username, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RemoveRemote 删除远程仓库
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function RemoveRemote(path, name) {
|
||||||
|
return $Call.ByID(2915623022, path, name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RemoveUser 从平台删除用户
|
* RemoveUser 从平台删除用户
|
||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
@@ -748,9 +789,11 @@ const $$createType21 = $models.ProjectStatus.createFrom;
|
|||||||
const $$createType22 = $Create.Nullable($$createType21);
|
const $$createType22 = $Create.Nullable($$createType21);
|
||||||
const $$createType23 = $models.TreeNode.createFrom;
|
const $$createType23 = $models.TreeNode.createFrom;
|
||||||
const $$createType24 = $Create.Array($$createType23);
|
const $$createType24 = $Create.Array($$createType23);
|
||||||
const $$createType25 = $models.StashInfo.createFrom;
|
const $$createType25 = $models.RemoteItem.createFrom;
|
||||||
const $$createType26 = $Create.Array($$createType25);
|
const $$createType26 = $Create.Array($$createType25);
|
||||||
const $$createType27 = $models.TagInfo.createFrom;
|
const $$createType27 = $models.StashInfo.createFrom;
|
||||||
const $$createType28 = $Create.Array($$createType27);
|
const $$createType28 = $Create.Array($$createType27);
|
||||||
const $$createType29 = $models.UserInfo.createFrom;
|
const $$createType29 = $models.TagInfo.createFrom;
|
||||||
const $$createType30 = $Create.Nullable($$createType29);
|
const $$createType30 = $Create.Array($$createType29);
|
||||||
|
const $$createType31 = $models.UserInfo.createFrom;
|
||||||
|
const $$createType32 = $Create.Nullable($$createType31);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export {
|
|||||||
PlatformInfo,
|
PlatformInfo,
|
||||||
ProjectOverview,
|
ProjectOverview,
|
||||||
ProjectStatus,
|
ProjectStatus,
|
||||||
|
RemoteItem,
|
||||||
StashInfo,
|
StashInfo,
|
||||||
TagInfo,
|
TagInfo,
|
||||||
TreeNode,
|
TreeNode,
|
||||||
|
|||||||
@@ -507,6 +507,20 @@ export class ProjectStatus {
|
|||||||
*/
|
*/
|
||||||
this["remoteUrl"] = "";
|
this["remoteUrl"] = "";
|
||||||
}
|
}
|
||||||
|
if (!("remotes" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {RemoteItem[]}
|
||||||
|
*/
|
||||||
|
this["remotes"] = [];
|
||||||
|
}
|
||||||
|
if (!("currentRemote" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["currentRemote"] = "";
|
||||||
|
}
|
||||||
if (!("changedFiles" in $$source)) {
|
if (!("changedFiles" in $$source)) {
|
||||||
/**
|
/**
|
||||||
* @member
|
* @member
|
||||||
@@ -525,14 +539,56 @@ export class ProjectStatus {
|
|||||||
*/
|
*/
|
||||||
static createFrom($$source = {}) {
|
static createFrom($$source = {}) {
|
||||||
const $$createField2_0 = $$createType1;
|
const $$createField2_0 = $$createType1;
|
||||||
|
const $$createField4_0 = $$createType3;
|
||||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
if ("remotes" in $$parsedSource) {
|
||||||
|
$$parsedSource["remotes"] = $$createField2_0($$parsedSource["remotes"]);
|
||||||
|
}
|
||||||
if ("changedFiles" in $$parsedSource) {
|
if ("changedFiles" in $$parsedSource) {
|
||||||
$$parsedSource["changedFiles"] = $$createField2_0($$parsedSource["changedFiles"]);
|
$$parsedSource["changedFiles"] = $$createField4_0($$parsedSource["changedFiles"]);
|
||||||
}
|
}
|
||||||
return new ProjectStatus(/** @type {Partial<ProjectStatus>} */($$parsedSource));
|
return new ProjectStatus(/** @type {Partial<ProjectStatus>} */($$parsedSource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RemoteItem 远程仓库信息
|
||||||
|
*/
|
||||||
|
export class RemoteItem {
|
||||||
|
/**
|
||||||
|
* Creates a new RemoteItem instance.
|
||||||
|
* @param {Partial<RemoteItem>} [$$source = {}] - The source object to create the RemoteItem.
|
||||||
|
*/
|
||||||
|
constructor($$source = {}) {
|
||||||
|
if (!("name" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["name"] = "";
|
||||||
|
}
|
||||||
|
if (!("url" in $$source)) {
|
||||||
|
/**
|
||||||
|
* @member
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this["url"] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new RemoteItem instance from a string or object.
|
||||||
|
* @param {any} [$$source = {}]
|
||||||
|
* @returns {RemoteItem}
|
||||||
|
*/
|
||||||
|
static createFrom($$source = {}) {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new RemoteItem(/** @type {Partial<RemoteItem>} */($$parsedSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StashInfo 贮藏信息
|
* StashInfo 贮藏信息
|
||||||
*/
|
*/
|
||||||
@@ -692,7 +748,7 @@ export class TreeNode {
|
|||||||
* @returns {TreeNode}
|
* @returns {TreeNode}
|
||||||
*/
|
*/
|
||||||
static createFrom($$source = {}) {
|
static createFrom($$source = {}) {
|
||||||
const $$createField4_0 = $$createType3;
|
const $$createField4_0 = $$createType5;
|
||||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
if ("children" in $$parsedSource) {
|
if ("children" in $$parsedSource) {
|
||||||
$$parsedSource["children"] = $$createField4_0($$parsedSource["children"]);
|
$$parsedSource["children"] = $$createField4_0($$parsedSource["children"]);
|
||||||
@@ -740,7 +796,9 @@ export class UserInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = FileInfo.createFrom;
|
const $$createType0 = RemoteItem.createFrom;
|
||||||
const $$createType1 = $Create.Array($$createType0);
|
const $$createType1 = $Create.Array($$createType0);
|
||||||
const $$createType2 = TreeNode.createFrom;
|
const $$createType2 = FileInfo.createFrom;
|
||||||
const $$createType3 = $Create.Array($$createType2);
|
const $$createType3 = $Create.Array($$createType2);
|
||||||
|
const $$createType4 = TreeNode.createFrom;
|
||||||
|
const $$createType5 = $Create.Array($$createType4);
|
||||||
|
|||||||
@@ -309,9 +309,17 @@ func (s *AppService) GetPlatformInfo(name string) (*PlatformInfo, error) {
|
|||||||
type ProjectStatus struct {
|
type ProjectStatus struct {
|
||||||
Branch string `json:"branch"`
|
Branch string `json:"branch"`
|
||||||
RemoteURL string `json:"remoteUrl"`
|
RemoteURL string `json:"remoteUrl"`
|
||||||
|
Remotes []RemoteItem `json:"remotes"`
|
||||||
|
CurrentRemote string `json:"currentRemote"`
|
||||||
ChangedFiles []FileInfo `json:"changedFiles"`
|
ChangedFiles []FileInfo `json:"changedFiles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoteItem 远程仓库信息
|
||||||
|
type RemoteItem struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
// FileInfo 文件信息
|
// FileInfo 文件信息
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
@@ -346,9 +354,24 @@ func (s *AppService) GetProjectStatus(path string) (*ProjectStatus, error) {
|
|||||||
|
|
||||||
remoteURL, _ := s.gitClient.RemoteURL(path)
|
remoteURL, _ := s.gitClient.RemoteURL(path)
|
||||||
|
|
||||||
|
// 获取所有 remote 列表
|
||||||
|
var remotes []RemoteItem
|
||||||
|
remoteList, remoteErr := s.gitClient.RemoteList(path)
|
||||||
|
if remoteErr == nil {
|
||||||
|
for _, r := range remoteList {
|
||||||
|
remotes = append(remotes, RemoteItem{Name: r.Name, URL: r.URL})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentRemote := "origin"
|
||||||
|
if len(remotes) > 0 {
|
||||||
|
currentRemote = remotes[0].Name
|
||||||
|
}
|
||||||
|
|
||||||
return &ProjectStatus{
|
return &ProjectStatus{
|
||||||
Branch: strings.TrimSpace(branch),
|
Branch: strings.TrimSpace(branch),
|
||||||
RemoteURL: remoteURL,
|
RemoteURL: remoteURL,
|
||||||
|
Remotes: remotes,
|
||||||
|
CurrentRemote: currentRemote,
|
||||||
ChangedFiles: []FileInfo{},
|
ChangedFiles: []FileInfo{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -515,22 +538,28 @@ func (s *AppService) GetFileDiffStaged(projectPath, filePath string) (string, er
|
|||||||
return s.gitClient.DiffStagedFile(projectPath, filePath)
|
return s.gitClient.DiffStagedFile(projectPath, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PullProject 拉取项目(当前分支)
|
// PullProject 拉取项目(当前分支,指定 remote)
|
||||||
func (s *AppService) PullProject(path string) (string, error) {
|
func (s *AppService) PullProject(path, remote string) (string, error) {
|
||||||
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
branch, err := s.gitClient.Branch(path)
|
branch, err := s.gitClient.Branch(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("获取当前分支失败: %w", err)
|
return "", fmt.Errorf("获取当前分支失败: %w", err)
|
||||||
}
|
}
|
||||||
return s.gitClient.Run(path, "pull", "origin", strings.TrimSpace(branch))
|
return s.gitClient.PullFrom(path, remote, strings.TrimSpace(branch))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushProject 推送项目(当前分支)
|
// PushProject 推送项目(当前分支,指定 remote)
|
||||||
func (s *AppService) PushProject(path string) (string, error) {
|
func (s *AppService) PushProject(path, remote string) (string, error) {
|
||||||
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
branch, err := s.gitClient.Branch(path)
|
branch, err := s.gitClient.Branch(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("获取当前分支失败: %w", err)
|
return "", fmt.Errorf("获取当前分支失败: %w", err)
|
||||||
}
|
}
|
||||||
return s.gitClient.Run(path, "push", "origin", strings.TrimSpace(branch))
|
return s.gitClient.PushTo(path, remote, strings.TrimSpace(branch))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommitDiff 获取指定提交的 diff
|
// GetCommitDiff 获取指定提交的 diff
|
||||||
@@ -581,9 +610,12 @@ func (s *AppService) GetCommitFileDiff(path, hash, filePath string) (string, err
|
|||||||
return s.gitClient.CommitFileDiff(path, hash, filePath)
|
return s.gitClient.CommitFileDiff(path, hash, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchProject 拉取远程信息
|
// FetchProject 拉取远程信息(指定 remote,空则 fetch --all)
|
||||||
func (s *AppService) FetchProject(path string) (string, error) {
|
func (s *AppService) FetchProject(path, remote string) (string, error) {
|
||||||
|
if remote == "" {
|
||||||
return s.gitClient.Fetch(path)
|
return s.gitClient.Fetch(path)
|
||||||
|
}
|
||||||
|
return s.gitClient.FetchRemote(path, remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitLog 提交记录
|
// CommitLog 提交记录
|
||||||
@@ -791,7 +823,7 @@ func (s *AppService) CreateTag(path, name, message string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTag 删除标签(本地+远程)
|
// DeleteTag 删除标签(本地+远程)
|
||||||
func (s *AppService) DeleteTag(path, name string) error {
|
func (s *AppService) DeleteTag(path, name, remote string) error {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
return fmt.Errorf("项目路径不存在: %s", path)
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
}
|
}
|
||||||
@@ -799,17 +831,20 @@ func (s *AppService) DeleteTag(path, name string) error {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
return fmt.Errorf("标签名不能为空")
|
return fmt.Errorf("标签名不能为空")
|
||||||
}
|
}
|
||||||
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
// 删除本地标签
|
// 删除本地标签
|
||||||
if _, err := s.gitClient.DeleteTag(path, name); err != nil {
|
if _, err := s.gitClient.DeleteTag(path, name); err != nil {
|
||||||
return fmt.Errorf("删除本地标签失败: %w", err)
|
return fmt.Errorf("删除本地标签失败: %w", err)
|
||||||
}
|
}
|
||||||
// 尝试删除远程标签(忽略错误,可能未推送过)
|
// 尝试删除远程标签(忽略错误,可能未推送过)
|
||||||
s.gitClient.DeleteRemoteTag(path, name)
|
s.gitClient.DeleteRemoteTagFrom(path, remote, name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushTag 推送标签到远程
|
// PushTag 推送标签到远程
|
||||||
func (s *AppService) PushTag(path, name string) error {
|
func (s *AppService) PushTag(path, name, remote string) error {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
return fmt.Errorf("项目路径不存在: %s", path)
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
}
|
}
|
||||||
@@ -817,7 +852,10 @@ func (s *AppService) PushTag(path, name string) error {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
return fmt.Errorf("标签名不能为空")
|
return fmt.Errorf("标签名不能为空")
|
||||||
}
|
}
|
||||||
_, err := s.gitClient.PushTag(path, name)
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.PushTagTo(path, remote, name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,11 +907,17 @@ func (s *AppService) MergeBranch(path, branch string) (string, error) {
|
|||||||
// --- 远程分支管理 ---
|
// --- 远程分支管理 ---
|
||||||
|
|
||||||
// GetRemoteBranches 获取远程分支列表
|
// GetRemoteBranches 获取远程分支列表
|
||||||
func (s *AppService) GetRemoteBranches(path string) ([]BranchInfo, error) {
|
func (s *AppService) GetRemoteBranches(path, remote string) ([]BranchInfo, error) {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("项目路径不存在: %s", path)
|
return nil, fmt.Errorf("项目路径不存在: %s", path)
|
||||||
}
|
}
|
||||||
out, err := s.gitClient.RemoteBranchList(path)
|
var out string
|
||||||
|
var err error
|
||||||
|
if remote == "" {
|
||||||
|
out, err = s.gitClient.RemoteBranchList(path)
|
||||||
|
} else {
|
||||||
|
out, err = s.gitClient.RemoteBranchListByRemote(path, remote)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("获取远程分支列表失败: %w", err)
|
return nil, fmt.Errorf("获取远程分支列表失败: %w", err)
|
||||||
}
|
}
|
||||||
@@ -907,7 +951,7 @@ func (s *AppService) CheckoutRemoteBranch(path, remoteBranch string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRemoteBranch 删除远程分支
|
// DeleteRemoteBranch 删除远程分支
|
||||||
func (s *AppService) DeleteRemoteBranch(path, branch string) error {
|
func (s *AppService) DeleteRemoteBranch(path, branch, remote string) error {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
return fmt.Errorf("项目路径不存在: %s", path)
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
}
|
}
|
||||||
@@ -915,12 +959,15 @@ func (s *AppService) DeleteRemoteBranch(path, branch string) error {
|
|||||||
if branch == "" {
|
if branch == "" {
|
||||||
return fmt.Errorf("分支名不能为空")
|
return fmt.Errorf("分支名不能为空")
|
||||||
}
|
}
|
||||||
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
// origin/feature -> feature
|
// origin/feature -> feature
|
||||||
localName := branch
|
localName := branch
|
||||||
if idx := strings.Index(branch, "/"); idx != -1 {
|
if idx := strings.Index(branch, "/"); idx != -1 {
|
||||||
localName = branch[idx+1:]
|
localName = branch[idx+1:]
|
||||||
}
|
}
|
||||||
_, err := s.gitClient.DeleteRemoteBranch(path, localName)
|
_, err := s.gitClient.DeleteRemoteBranchFrom(path, remote, localName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1050,6 +1097,54 @@ func (s *AppService) UpdateAppSettings(logLevel string) error {
|
|||||||
return config.SaveConfig(s.config)
|
return config.SaveConfig(s.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 远程仓库管理 ---
|
||||||
|
|
||||||
|
// GetRemotes 获取项目所有远程仓库列表
|
||||||
|
func (s *AppService) GetRemotes(path string) ([]RemoteItem, error) {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
remoteList, err := s.gitClient.RemoteList(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取远程仓库列表失败: %w", err)
|
||||||
|
}
|
||||||
|
var remotes []RemoteItem
|
||||||
|
for _, r := range remoteList {
|
||||||
|
remotes = append(remotes, RemoteItem{Name: r.Name, URL: r.URL})
|
||||||
|
}
|
||||||
|
return remotes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRemote 添加远程仓库
|
||||||
|
func (s *AppService) AddRemote(path, name, url string) error {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("项目路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
url = strings.TrimSpace(url)
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("远程名称不能为空")
|
||||||
|
}
|
||||||
|
if url == "" {
|
||||||
|
return fmt.Errorf("远程地址不能为空")
|
||||||
|
}
|
||||||
|
_, err := s.gitClient.AddRemote(path, name, url)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRemote 删除远程仓库
|
||||||
|
func (s *AppService) RemoveRemote(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.RemoveRemote(path, name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// --- 冲突处理 ---
|
// --- 冲突处理 ---
|
||||||
|
|
||||||
// ConflictFileInfo 冲突文件信息
|
// ConflictFileInfo 冲突文件信息
|
||||||
@@ -1211,7 +1306,10 @@ type BatchPullResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BatchPull 批量拉取指定项目
|
// BatchPull 批量拉取指定项目
|
||||||
func (s *AppService) BatchPull(paths []string) []BatchPullResult {
|
func (s *AppService) BatchPull(paths []string, remote string) []BatchPullResult {
|
||||||
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
var results []BatchPullResult
|
var results []BatchPullResult
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
result := BatchPullResult{Path: path}
|
result := BatchPullResult{Path: path}
|
||||||
@@ -1229,7 +1327,7 @@ func (s *AppService) BatchPull(paths []string) []BatchPullResult {
|
|||||||
results = append(results, result)
|
results = append(results, result)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err = s.gitClient.Run(path, "pull", "origin", strings.TrimSpace(branch))
|
_, err = s.gitClient.PullFrom(path, remote, strings.TrimSpace(branch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Message = err.Error()
|
result.Message = err.Error()
|
||||||
} else {
|
} else {
|
||||||
@@ -1242,7 +1340,10 @@ func (s *AppService) BatchPull(paths []string) []BatchPullResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BatchPush 批量推送指定项目
|
// BatchPush 批量推送指定项目
|
||||||
func (s *AppService) BatchPush(paths []string) []BatchPullResult {
|
func (s *AppService) BatchPush(paths []string, remote string) []BatchPullResult {
|
||||||
|
if remote == "" {
|
||||||
|
remote = "origin"
|
||||||
|
}
|
||||||
var results []BatchPullResult
|
var results []BatchPullResult
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
result := BatchPullResult{Path: path}
|
result := BatchPullResult{Path: path}
|
||||||
@@ -1259,7 +1360,7 @@ func (s *AppService) BatchPush(paths []string) []BatchPullResult {
|
|||||||
results = append(results, result)
|
results = append(results, result)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err = s.gitClient.Run(path, "push", "origin", strings.TrimSpace(branch))
|
_, err = s.gitClient.PushTo(path, remote, strings.TrimSpace(branch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Message = err.Error()
|
result.Message = err.Error()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -60,6 +60,21 @@ func (g *GitClient) Push(path string) (string, error) {
|
|||||||
return g.Run(path, "push")
|
return g.Run(path, "push")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushTo 推送到指定远程
|
||||||
|
func (g *GitClient) PushTo(path, remote, branch string) (string, error) {
|
||||||
|
return g.Run(path, "push", remote, branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullFrom 从指定远程拉取
|
||||||
|
func (g *GitClient) PullFrom(path, remote, branch string) (string, error) {
|
||||||
|
return g.Run(path, "pull", remote, branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchRemote 拉取指定远程信息
|
||||||
|
func (g *GitClient) FetchRemote(path, remote string) (string, error) {
|
||||||
|
return g.Run(path, "fetch", remote)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GitClient) Status(path string) (string, error) {
|
func (g *GitClient) Status(path string) (string, error) {
|
||||||
return g.Run(path, "status")
|
return g.Run(path, "status")
|
||||||
}
|
}
|
||||||
@@ -93,6 +108,47 @@ func (g *GitClient) Fetch(path string) (string, error) {
|
|||||||
return g.Run(path, "fetch", "--all")
|
return g.Run(path, "fetch", "--all")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoteList 获取所有远程仓库列表 (git remote -v)
|
||||||
|
type RemoteInfo struct {
|
||||||
|
Name string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitClient) RemoteList(path string) ([]RemoteInfo, error) {
|
||||||
|
out, err := g.Run(path, "remote", "-v")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var remotes []RemoteInfo
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := parts[0]
|
||||||
|
if seen[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[name] = true
|
||||||
|
remotes = append(remotes, RemoteInfo{Name: name, URL: parts[1]})
|
||||||
|
}
|
||||||
|
return remotes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRemote 添加远程仓库
|
||||||
|
func (g *GitClient) AddRemote(path, name, url string) (string, error) {
|
||||||
|
return g.Run(path, "remote", "add", name, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRemote 删除远程仓库
|
||||||
|
func (g *GitClient) RemoveRemote(path, name string) (string, error) {
|
||||||
|
return g.Run(path, "remote", "remove", name)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GitClient) RemoteURL(path string) (string, error) {
|
func (g *GitClient) RemoteURL(path string) (string, error) {
|
||||||
out, err := g.Run(path, "config", "--get", "remote.origin.url")
|
out, err := g.Run(path, "config", "--get", "remote.origin.url")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -141,11 +197,16 @@ func (g *GitClient) Log(path string, count int) (string, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpushedCommits 获取当前分支上未推送到远程的提交哈希列表
|
// UnpushedCommits 获取当前分支上未推送到远程的提交哈希列表(默认 origin)
|
||||||
func (g *GitClient) UnpushedCommits(path, branch string) (string, error) {
|
func (g *GitClient) UnpushedCommits(path, branch string) (string, error) {
|
||||||
return g.Run(path, "rev-list", "origin/"+branch+"..HEAD")
|
return g.Run(path, "rev-list", "origin/"+branch+"..HEAD")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnpushedCommitsTo 获取当前分支上未推送到指定远程的提交哈希列表
|
||||||
|
func (g *GitClient) UnpushedCommitsTo(path, remote, branch string) (string, error) {
|
||||||
|
return g.Run(path, "rev-list", remote+"/"+branch+"..HEAD")
|
||||||
|
}
|
||||||
|
|
||||||
// RevertCommit 撤回指定提交(创建一个反向提交)
|
// RevertCommit 撤回指定提交(创建一个反向提交)
|
||||||
func (g *GitClient) RevertCommit(path, hash string) (string, error) {
|
func (g *GitClient) RevertCommit(path, hash string) (string, error) {
|
||||||
return g.Run(path, "revert", "--no-edit", hash)
|
return g.Run(path, "revert", "--no-edit", hash)
|
||||||
@@ -167,16 +228,26 @@ func (g *GitClient) DeleteTag(path, name string) (string, error) {
|
|||||||
return g.Run(path, "tag", "-d", name)
|
return g.Run(path, "tag", "-d", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushTag 推送标签到远程
|
// PushTag 推送标签到远程(默认 origin)
|
||||||
func (g *GitClient) PushTag(path, name string) (string, error) {
|
func (g *GitClient) PushTag(path, name string) (string, error) {
|
||||||
return g.Run(path, "push", "origin", name)
|
return g.Run(path, "push", "origin", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRemoteTag 删除远程标签
|
// PushTagTo 推送标签到指定远程
|
||||||
|
func (g *GitClient) PushTagTo(path, remote, name string) (string, error) {
|
||||||
|
return g.Run(path, "push", remote, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRemoteTag 删除远程标签(默认 origin)
|
||||||
func (g *GitClient) DeleteRemoteTag(path, name string) (string, error) {
|
func (g *GitClient) DeleteRemoteTag(path, name string) (string, error) {
|
||||||
return g.Run(path, "push", "origin", "--delete", name)
|
return g.Run(path, "push", "origin", "--delete", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteRemoteTagFrom 删除指定远程的标签
|
||||||
|
func (g *GitClient) DeleteRemoteTagFrom(path, remote, name string) (string, error) {
|
||||||
|
return g.Run(path, "push", remote, "--delete", name)
|
||||||
|
}
|
||||||
|
|
||||||
// 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)")
|
||||||
@@ -368,16 +439,26 @@ func (g *GitClient) MergeBranch(path, branch string) (string, error) {
|
|||||||
return g.Run(path, "merge", branch)
|
return g.Run(path, "merge", branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRemoteBranch 删除远程分支
|
// DeleteRemoteBranch 删除远程分支(默认 origin)
|
||||||
func (g *GitClient) DeleteRemoteBranch(path, branch string) (string, error) {
|
func (g *GitClient) DeleteRemoteBranch(path, branch string) (string, error) {
|
||||||
return g.Run(path, "push", "origin", "--delete", branch)
|
return g.Run(path, "push", "origin", "--delete", branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteRemoteBranchFrom 删除指定远程的分支
|
||||||
|
func (g *GitClient) DeleteRemoteBranchFrom(path, remote, branch string) (string, error) {
|
||||||
|
return g.Run(path, "push", remote, "--delete", branch)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoteBranchList 获取所有远程分支
|
// RemoteBranchList 获取所有远程分支
|
||||||
func (g *GitClient) RemoteBranchList(path string) (string, error) {
|
func (g *GitClient) RemoteBranchList(path string) (string, error) {
|
||||||
return g.Run(path, "branch", "-r", "--format=%(refname:short)")
|
return g.Run(path, "branch", "-r", "--format=%(refname:short)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoteBranchListByRemote 获取指定远程的分支
|
||||||
|
func (g *GitClient) RemoteBranchListByRemote(path, remote string) (string, error) {
|
||||||
|
return g.Run(path, "branch", "-r", "--list", remote+"/*", "--format=%(refname:short)")
|
||||||
|
}
|
||||||
|
|
||||||
// CheckoutNewBranch 从远程分支检出新本地分支
|
// CheckoutNewBranch 从远程分支检出新本地分支
|
||||||
func (g *GitClient) CheckoutNewBranch(path, localBranch, remoteBranch string) (string, error) {
|
func (g *GitClient) CheckoutNewBranch(path, localBranch, remoteBranch string) (string, error) {
|
||||||
return g.Run(path, "checkout", "-b", localBranch, remoteBranch)
|
return g.Run(path, "checkout", "-b", localBranch, remoteBranch)
|
||||||
|
|||||||
Reference in New Issue
Block a user