目标路径:
{{ cloneForm.parentDir }}/{{ cloneForm.name }}
@@ -647,53 +631,109 @@ onMounted(() => {
-
+
-
+
+
+
+
+
+ 📁 文件夹
+ 🐙 GitHub
+ 🟠 Gitee
+ 🍵 Gitea
+
+
+
+
+
+
+
+
+
+ 暂无凭证
+
+
+
+
+
+
+ {{ cred.platform }}
+
+
{{ cred.username }}
+
+
{{ cred.baseUrl }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GitHub
+ Gitee
+ Gitea
+ GitLab
+
+
+
Base URL (自建平台需要填写)
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Token (可选,用于 API 认证)
-
+
@@ -815,7 +855,7 @@ onMounted(() => {
gap: 6px;
}
-.platform-logo {
+.group-logo {
width: 16px;
height: 16px;
display: inline-flex;
@@ -824,7 +864,7 @@ onMounted(() => {
flex-shrink: 0;
}
-.platform-logo :deep(svg) {
+.group-logo :deep(svg) {
width: 16px;
height: 16px;
}
@@ -967,4 +1007,41 @@ onMounted(() => {
0%, 100% { opacity: 0.4; }
50% { opacity: 0.8; }
}
+
+/* 凭证列表 */
+.credential-list {
+ max-height: 400px;
+ overflow-y: auto;
+}
+
+.credential-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 4px;
+ border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.04));
+}
+
+.credential-item:hover {
+ background: var(--bg-hover, rgba(255,255,255,0.04));
+}
+
+.credential-info {
+ flex: 1;
+ min-width: 0;
+}
+
+.credential-name {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ font-weight: 500;
+}
+
+.credential-url {
+ font-size: 11px;
+ color: var(--text-muted);
+ margin-top: 2px;
+}
diff --git a/frontend/bindings/github.com/zhuy1228/GitPilot/config/index.js b/frontend/bindings/github.com/zhuy1228/GitPilot/config/index.js
index 471e641..1293ae2 100644
--- a/frontend/bindings/github.com/zhuy1228/GitPilot/config/index.js
+++ b/frontend/bindings/github.com/zhuy1228/GitPilot/config/index.js
@@ -3,5 +3,6 @@
// This file is automatically generated. DO NOT EDIT
export {
+ Group,
Settings
} from "./models.js";
diff --git a/frontend/bindings/github.com/zhuy1228/GitPilot/config/models.js b/frontend/bindings/github.com/zhuy1228/GitPilot/config/models.js
index 6995763..d720b37 100644
--- a/frontend/bindings/github.com/zhuy1228/GitPilot/config/models.js
+++ b/frontend/bindings/github.com/zhuy1228/GitPilot/config/models.js
@@ -6,6 +6,48 @@
// @ts-ignore: Unused imports
import { Create as $Create } from "@wailsio/runtime";
+/**
+ * Group 项目分组
+ */
+export class Group {
+ /**
+ * Creates a new Group instance.
+ * @param {Partial
} [$$source = {}] - The source object to create the Group.
+ */
+ constructor($$source = {}) {
+ if (!("Name" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["Name"] = "";
+ }
+ if (!("Icon" in $$source)) {
+ /**
+ * 可选图标标识
+ * @member
+ * @type {string}
+ */
+ this["Icon"] = "";
+ }
+
+ Object.assign(this, $$source);
+ }
+
+ /**
+ * Creates a new Group instance from a string or object.
+ * @param {any} [$$source = {}]
+ * @returns {Group}
+ */
+ static createFrom($$source = {}) {
+ let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
+ return new Group(/** @type {Partial} */($$parsedSource));
+ }
+}
+
+/**
+ * Settings 应用设置
+ */
export class Settings {
/**
* Creates a new Settings instance.
diff --git a/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/appservice.js b/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/appservice.js
index beae97e..45476dc 100644
--- a/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/appservice.js
+++ b/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/appservice.js
@@ -32,25 +32,36 @@ export function AbortMerge(path) {
}
/**
- * AddPlatform 添加新平台
- * @param {string} name
+ * AddCredential 添加凭证
+ * @param {string} platform
* @param {string} baseURL
+ * @param {string} username
+ * @param {string} token
* @returns {$CancellablePromise}
*/
-export function AddPlatform(name, baseURL) {
- return $Call.ByID(526003212, name, baseURL);
+export function AddCredential(platform, baseURL, username, token) {
+ return $Call.ByID(1627284202, platform, baseURL, username, token);
}
/**
- * AddProject 添加项目到指定平台/用户下
- * @param {string} platform
- * @param {string} username
+ * AddGroup 添加分组
* @param {string} name
- * @param {string} path
+ * @param {string} icon
* @returns {$CancellablePromise}
*/
-export function AddProject(platform, username, name, path) {
- return $Call.ByID(2299402672, platform, username, name, path);
+export function AddGroup(name, icon) {
+ return $Call.ByID(4237035530, name, icon);
+}
+
+/**
+ * AddProject 添加项目
+ * @param {string} name
+ * @param {string} path
+ * @param {string} group
+ * @returns {$CancellablePromise}
+ */
+export function AddProject(name, path, group) {
+ return $Call.ByID(2299402672, name, path, group);
}
/**
@@ -64,17 +75,6 @@ export function AddRemote(path, name, url) {
return $Call.ByID(3697916441, path, name, url);
}
-/**
- * AddUser 添加用户到指定平台
- * @param {string} platform
- * @param {string} username
- * @param {string} token
- * @returns {$CancellablePromise}
- */
-export function AddUser(platform, username, token) {
- return $Call.ByID(2264426704, platform, username, token);
-}
-
/**
* BatchPull 批量拉取指定项目
* @param {string[]} paths
@@ -120,16 +120,15 @@ export function CheckoutRemoteBranch(path, remoteBranch) {
}
/**
- * CloneProject 克隆远程仓库到本地目录,并添加到项目树
- * @param {string} platform
- * @param {string} username
+ * CloneProject 克隆远程仓库到本地目录,并添加到项目列表
* @param {string} repoURL
* @param {string} parentDir
* @param {string} name
+ * @param {string} group
* @returns {$CancellablePromise}
*/
-export function CloneProject(platform, username, repoURL, parentDir, name) {
- return $Call.ByID(2347019414, platform, username, repoURL, parentDir, name);
+export function CloneProject(repoURL, parentDir, name, group) {
+ return $Call.ByID(2347019414, repoURL, parentDir, name, group);
}
/**
@@ -313,6 +312,16 @@ export function GetConflictFiles(path) {
}));
}
+/**
+ * GetCredentials 获取所有凭证
+ * @returns {$CancellablePromise<$models.CredentialInfo[]>}
+ */
+export function GetCredentials() {
+ return $Call.ByID(4214051290).then(/** @type {($result: any) => any} */(($result) => {
+ return $$createType16($result);
+ }));
+}
+
/**
* GetFileContent 获取文件内容
* @param {string} projectPath
@@ -349,18 +358,17 @@ export function GetFileDiffStaged(projectPath, filePath) {
*/
export function GetGitGlobalConfig() {
return $Call.ByID(154811497).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType16($result);
+ return $$createType18($result);
}));
}
/**
- * GetPlatformInfo 获取平台信息
- * @param {string} name
- * @returns {$CancellablePromise<$models.PlatformInfo | null>}
+ * GetGroups 获取所有分组
+ * @returns {$CancellablePromise}
*/
-export function GetPlatformInfo(name) {
- return $Call.ByID(2668095547, name).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType18($result);
+export function GetGroups() {
+ return $Call.ByID(1600884836).then(/** @type {($result: any) => any} */(($result) => {
+ return $$createType20($result);
}));
}
@@ -371,10 +379,19 @@ export function GetPlatformInfo(name) {
*/
export function GetProjectChangedFiles(path) {
return $Call.ByID(2302591462, path).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType20($result);
+ return $$createType22($result);
}));
}
+/**
+ * GetProjectProxy 获取项目代理设置(nil 表示跟随全局)
+ * @param {string} path
+ * @returns {$CancellablePromise}
+ */
+export function GetProjectProxy(path) {
+ return $Call.ByID(2515497911, path);
+}
+
/**
* GetProjectStatus 获取项目 git 状态
* @param {string} path
@@ -382,17 +399,17 @@ export function GetProjectChangedFiles(path) {
*/
export function GetProjectStatus(path) {
return $Call.ByID(3451089027, path).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType22($result);
+ return $$createType24($result);
}));
}
/**
- * GetProjectTree 获取项目树,供前端侧边栏渲染
+ * GetProjectTree 获取项目树,按分组组织
* @returns {$CancellablePromise<$models.TreeNode[]>}
*/
export function GetProjectTree() {
return $Call.ByID(651189689).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType24($result);
+ return $$createType26($result);
}));
}
@@ -415,7 +432,7 @@ export function GetRemoteBranches(path, remote) {
*/
export function GetRemotes(path) {
return $Call.ByID(975520027, path).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType26($result);
+ return $$createType28($result);
}));
}
@@ -426,7 +443,7 @@ export function GetRemotes(path) {
*/
export function GetStashList(path) {
return $Call.ByID(1948257945, path).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType28($result);
+ return $$createType30($result);
}));
}
@@ -437,18 +454,6 @@ export function GetStashList(path) {
*/
export function GetTags(path) {
return $Call.ByID(3979169621, path).then(/** @type {($result: any) => any} */(($result) => {
- return $$createType30($result);
- }));
-}
-
-/**
- * GetUserInfo 获取用户信息
- * @param {string} platform
- * @param {string} username
- * @returns {$CancellablePromise<$models.UserInfo | null>}
- */
-export function GetUserInfo(platform, username) {
- return $Call.ByID(2674655707, platform, username).then(/** @type {($result: any) => any} */(($result) => {
return $$createType32($result);
}));
}
@@ -482,6 +487,16 @@ export function MergeBranch(path, branch) {
return $Call.ByID(278161076, path, branch);
}
+/**
+ * MoveProjectToGroup 移动项目到指定分组
+ * @param {string} path
+ * @param {string} group
+ * @returns {$CancellablePromise}
+ */
+export function MoveProjectToGroup(path, group) {
+ return $Call.ByID(1699187666, path, group);
+}
+
/**
* PullProject 拉取项目(当前分支,指定 remote)
* @param {string} path
@@ -514,23 +529,54 @@ export function PushTag(path, name, remote) {
}
/**
- * RemovePlatform 删除平台
- * @param {string} name
- * @returns {$CancellablePromise}
+ * PushTagToAllRemotes 一键推送标签到所有远程仓库
+ * @param {string} path
+ * @param {string} tagName
+ * @returns {$CancellablePromise<$models.PushAllResult[]>}
*/
-export function RemovePlatform(name) {
- return $Call.ByID(2222448051, name);
+export function PushTagToAllRemotes(path, tagName) {
+ return $Call.ByID(2343581171, path, tagName).then(/** @type {($result: any) => any} */(($result) => {
+ return $$createType34($result);
+ }));
}
/**
- * RemoveProject 从指定平台/用户下删除项目
+ * PushToAllRemotes 一键推送到所有远程仓库(当前分支)
+ * @param {string} path
+ * @returns {$CancellablePromise<$models.PushAllResult[]>}
+ */
+export function PushToAllRemotes(path) {
+ return $Call.ByID(1027392731, path).then(/** @type {($result: any) => any} */(($result) => {
+ return $$createType34($result);
+ }));
+}
+
+/**
+ * RemoveCredential 删除凭证
* @param {string} platform
* @param {string} username
+ * @returns {$CancellablePromise}
+ */
+export function RemoveCredential(platform, username) {
+ return $Call.ByID(414538393, platform, username);
+}
+
+/**
+ * RemoveGroup 删除分组(分组下的项目变为"未分组")
* @param {string} name
* @returns {$CancellablePromise}
*/
-export function RemoveProject(platform, username, name) {
- return $Call.ByID(2748109581, platform, username, name);
+export function RemoveGroup(name) {
+ return $Call.ByID(3268966203, name);
+}
+
+/**
+ * RemoveProject 删除项目(按路径匹配)
+ * @param {string} path
+ * @returns {$CancellablePromise}
+ */
+export function RemoveProject(path) {
+ return $Call.ByID(2748109581, path);
}
/**
@@ -543,16 +589,6 @@ export function RemoveRemote(path, name) {
return $Call.ByID(2915623022, path, name);
}
-/**
- * RemoveUser 从平台删除用户
- * @param {string} platform
- * @param {string} username
- * @returns {$CancellablePromise}
- */
-export function RemoveUser(platform, username) {
- return $Call.ByID(1409517275, platform, username);
-}
-
/**
* ResetProject 版本回滚(git reset)
* mode: "hard"(丢弃所有更改), "soft"(保留更改到暂存区), "mixed"(保留更改到工作区)
@@ -644,6 +680,17 @@ export function SetGitGlobalConfig(name, email) {
return $Call.ByID(3875064981, name, email);
}
+/**
+ * SetProjectProxy 设置项目代理开关
+ * useProxy: true=强制启用, false=强制禁用, nil(传空)=跟随全局
+ * @param {string} path
+ * @param {boolean | null} useProxy
+ * @returns {$CancellablePromise}
+ */
+export function SetProjectProxy(path, useProxy) {
+ return $Call.ByID(4094650651, path, useProxy);
+}
+
/**
* StageAll 暂存所有变更文件
* @param {string} path
@@ -742,25 +789,26 @@ export function UpdateAppSettings(logLevel) {
}
/**
- * UpdatePlatform 修改平台信息(base_url)
- * @param {string} name
- * @param {string} baseURL
- * @returns {$CancellablePromise}
- */
-export function UpdatePlatform(name, baseURL) {
- return $Call.ByID(3258014550, name, baseURL);
-}
-
-/**
- * UpdateUser 修改用户信息(用户名、token)
+ * UpdateCredential 更新凭证
* @param {string} platform
- * @param {string} oldUsername
- * @param {string} newUsername
+ * @param {string} username
+ * @param {string} baseURL
* @param {string} token
* @returns {$CancellablePromise}
*/
-export function UpdateUser(platform, oldUsername, newUsername, token) {
- return $Call.ByID(1631729342, platform, oldUsername, newUsername, token);
+export function UpdateCredential(platform, username, baseURL, token) {
+ return $Call.ByID(2448329924, platform, username, baseURL, token);
+}
+
+/**
+ * UpdateGroup 修改分组
+ * @param {string} oldName
+ * @param {string} newName
+ * @param {string} icon
+ * @returns {$CancellablePromise}
+ */
+export function UpdateGroup(oldName, newName, icon) {
+ return $Call.ByID(1322206248, oldName, newName, icon);
}
// Private type creation functions
@@ -779,21 +827,23 @@ const $$createType11 = $models.CommitLog.createFrom;
const $$createType12 = $Create.Array($$createType11);
const $$createType13 = $models.ConflictFileInfo.createFrom;
const $$createType14 = $Create.Array($$createType13);
-const $$createType15 = $models.GitConfig.createFrom;
-const $$createType16 = $Create.Nullable($$createType15);
-const $$createType17 = $models.PlatformInfo.createFrom;
+const $$createType15 = $models.CredentialInfo.createFrom;
+const $$createType16 = $Create.Array($$createType15);
+const $$createType17 = $models.GitConfig.createFrom;
const $$createType18 = $Create.Nullable($$createType17);
-const $$createType19 = $models.FileInfo.createFrom;
+const $$createType19 = config$0.Group.createFrom;
const $$createType20 = $Create.Array($$createType19);
-const $$createType21 = $models.ProjectStatus.createFrom;
-const $$createType22 = $Create.Nullable($$createType21);
-const $$createType23 = $models.TreeNode.createFrom;
-const $$createType24 = $Create.Array($$createType23);
-const $$createType25 = $models.RemoteItem.createFrom;
+const $$createType21 = $models.FileInfo.createFrom;
+const $$createType22 = $Create.Array($$createType21);
+const $$createType23 = $models.ProjectStatus.createFrom;
+const $$createType24 = $Create.Nullable($$createType23);
+const $$createType25 = $models.TreeNode.createFrom;
const $$createType26 = $Create.Array($$createType25);
-const $$createType27 = $models.StashInfo.createFrom;
+const $$createType27 = $models.RemoteItem.createFrom;
const $$createType28 = $Create.Array($$createType27);
-const $$createType29 = $models.TagInfo.createFrom;
+const $$createType29 = $models.StashInfo.createFrom;
const $$createType30 = $Create.Array($$createType29);
-const $$createType31 = $models.UserInfo.createFrom;
-const $$createType32 = $Create.Nullable($$createType31);
+const $$createType31 = $models.TagInfo.createFrom;
+const $$createType32 = $Create.Array($$createType31);
+const $$createType33 = $models.PushAllResult.createFrom;
+const $$createType34 = $Create.Array($$createType33);
diff --git a/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/index.js b/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/index.js
index 5e6d162..64fb09e 100644
--- a/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/index.js
+++ b/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/index.js
@@ -13,15 +13,15 @@ export {
CommitFileInfo,
CommitLog,
ConflictFileInfo,
+ CredentialInfo,
FileInfo,
GitConfig,
GitStatus,
- PlatformInfo,
ProjectOverview,
ProjectStatus,
+ PushAllResult,
RemoteItem,
StashInfo,
TagInfo,
- TreeNode,
- UserInfo
+ TreeNode
} from "./models.js";
diff --git a/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/models.js b/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/models.js
index c1e18bb..dee7268 100644
--- a/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/models.js
+++ b/frontend/bindings/github.com/zhuy1228/GitPilot/internal/app/models.js
@@ -238,6 +238,58 @@ export class ConflictFileInfo {
}
}
+/**
+ * CredentialInfo 凭证信息(前端展示用)
+ */
+export class CredentialInfo {
+ /**
+ * Creates a new CredentialInfo instance.
+ * @param {Partial} [$$source = {}] - The source object to create the CredentialInfo.
+ */
+ constructor($$source = {}) {
+ if (!("platform" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["platform"] = "";
+ }
+ if (!("baseUrl" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["baseUrl"] = "";
+ }
+ if (!("username" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["username"] = "";
+ }
+ if (!("token" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["token"] = "";
+ }
+
+ Object.assign(this, $$source);
+ }
+
+ /**
+ * Creates a new CredentialInfo instance from a string or object.
+ * @param {any} [$$source = {}]
+ * @returns {CredentialInfo}
+ */
+ static createFrom($$source = {}) {
+ let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
+ return new CredentialInfo(/** @type {Partial} */($$parsedSource));
+ }
+}
+
/**
* FileInfo 文件信息
*/
@@ -373,44 +425,6 @@ export class GitStatus {
}
}
-/**
- * PlatformInfo 平台信息
- */
-export class PlatformInfo {
- /**
- * Creates a new PlatformInfo instance.
- * @param {Partial} [$$source = {}] - The source object to create the PlatformInfo.
- */
- constructor($$source = {}) {
- if (!("name" in $$source)) {
- /**
- * @member
- * @type {string}
- */
- this["name"] = "";
- }
- if (!("baseUrl" in $$source)) {
- /**
- * @member
- * @type {string}
- */
- this["baseUrl"] = "";
- }
-
- Object.assign(this, $$source);
- }
-
- /**
- * Creates a new PlatformInfo instance from a string or object.
- * @param {any} [$$source = {}]
- * @returns {PlatformInfo}
- */
- static createFrom($$source = {}) {
- let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
- return new PlatformInfo(/** @type {Partial} */($$parsedSource));
- }
-}
-
/**
* ProjectOverview 项目概览信息(轻量级)
*/
@@ -528,6 +542,14 @@ export class ProjectStatus {
*/
this["changedFiles"] = [];
}
+ if (!("useProxy" in $$source)) {
+ /**
+ * nil=跟随全局, true=开启, false=关闭
+ * @member
+ * @type {boolean | null}
+ */
+ this["useProxy"] = null;
+ }
Object.assign(this, $$source);
}
@@ -551,6 +573,51 @@ export class ProjectStatus {
}
}
+/**
+ * PushAllResult 一键多端推送结果
+ */
+export class PushAllResult {
+ /**
+ * Creates a new PushAllResult instance.
+ * @param {Partial} [$$source = {}] - The source object to create the PushAllResult.
+ */
+ constructor($$source = {}) {
+ if (!("remote" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["remote"] = "";
+ }
+ if (!("success" in $$source)) {
+ /**
+ * @member
+ * @type {boolean}
+ */
+ this["success"] = false;
+ }
+ if (!("message" in $$source)) {
+ /**
+ * @member
+ * @type {string}
+ */
+ this["message"] = "";
+ }
+
+ Object.assign(this, $$source);
+ }
+
+ /**
+ * Creates a new PushAllResult instance from a string or object.
+ * @param {any} [$$source = {}]
+ * @returns {PushAllResult}
+ */
+ static createFrom($$source = {}) {
+ let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
+ return new PushAllResult(/** @type {Partial} */($$parsedSource));
+ }
+}
+
/**
* RemoteItem 远程仓库信息
*/
@@ -718,7 +785,7 @@ export class TreeNode {
}
if (!("type" in $$source)) {
/**
- * platform, user, project
+ * group, project
* @member
* @type {string}
*/
@@ -731,6 +798,13 @@ export class TreeNode {
*/
this["path"] = undefined;
}
+ if (/** @type {any} */(false)) {
+ /**
+ * @member
+ * @type {string | undefined}
+ */
+ this["icon"] = undefined;
+ }
if (/** @type {any} */(false)) {
/**
* @member
@@ -748,53 +822,15 @@ export class TreeNode {
* @returns {TreeNode}
*/
static createFrom($$source = {}) {
- const $$createField4_0 = $$createType5;
+ const $$createField5_0 = $$createType5;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("children" in $$parsedSource) {
- $$parsedSource["children"] = $$createField4_0($$parsedSource["children"]);
+ $$parsedSource["children"] = $$createField5_0($$parsedSource["children"]);
}
return new TreeNode(/** @type {Partial} */($$parsedSource));
}
}
-/**
- * UserInfo 用户信息
- */
-export class UserInfo {
- /**
- * Creates a new UserInfo instance.
- * @param {Partial} [$$source = {}] - The source object to create the UserInfo.
- */
- constructor($$source = {}) {
- if (!("username" in $$source)) {
- /**
- * @member
- * @type {string}
- */
- this["username"] = "";
- }
- if (!("token" in $$source)) {
- /**
- * @member
- * @type {string}
- */
- this["token"] = "";
- }
-
- Object.assign(this, $$source);
- }
-
- /**
- * Creates a new UserInfo instance from a string or object.
- * @param {any} [$$source = {}]
- * @returns {UserInfo}
- */
- static createFrom($$source = {}) {
- let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
- return new UserInfo(/** @type {Partial} */($$parsedSource));
- }
-}
-
// Private type creation functions
const $$createType0 = RemoteItem.createFrom;
const $$createType1 = $Create.Array($$createType0);
diff --git a/internal/app/service.go b/internal/app/service.go
index 4fd5e5f..6f102c4 100644
--- a/internal/app/service.go
+++ b/internal/app/service.go
@@ -28,8 +28,10 @@ func New() *AppService {
if err != nil {
log.Printf("加载配置失败: %v, 使用默认配置", err)
cfg = &config.AppConfig{
- Platforms: make(map[string]config.Platform),
- Settings: config.Settings{Concurrency: 6, NetworkCheck: true, LogLevel: "info"},
+ Projects: []config.ProjectItem{},
+ Groups: []config.Group{},
+ Credentials: []config.Credential{},
+ Settings: config.Settings{Concurrency: 6, NetworkCheck: true, LogLevel: "info"},
}
}
return &AppService{
@@ -54,51 +56,91 @@ func (s *AppService) SelectDirectory() (string, error) {
return path, nil
}
-// --- 平台/项目 树形结构 ---
+// --- 项目/分组 树形结构 ---
// TreeNode 前端侧边栏树节点
type TreeNode struct {
Key string `json:"key"`
Label string `json:"label"`
- Type string `json:"type"` // platform, user, project
+ Type string `json:"type"` // group, project
Path string `json:"path,omitempty"`
+ Icon string `json:"icon,omitempty"`
Children []TreeNode `json:"children,omitempty"`
}
-// GetProjectTree 获取项目树,供前端侧边栏渲染
+// GetProjectTree 获取项目树,按分组组织
func (s *AppService) GetProjectTree() []TreeNode {
- var tree []TreeNode
- for platformName, platform := range s.config.Platforms {
- platformNode := TreeNode{
- Key: platformName,
- Label: platformName,
- Type: "platform",
+ // 按分组归类项目
+ groupProjects := make(map[string][]config.ProjectItem)
+ for _, proj := range s.config.Projects {
+ g := proj.Group
+ if g == "" {
+ g = "未分组"
}
- for _, user := range platform.Users {
- userNode := TreeNode{
- Key: platformName + "/" + user.Username,
- Label: user.Username,
- Type: "user",
- }
- for _, proj := range user.Projects {
- userNode.Children = append(userNode.Children, TreeNode{
- Key: platformName + "/" + user.Username + "/" + proj.Name,
- Label: proj.Name,
- Type: "project",
- Path: proj.Path,
- })
- }
- platformNode.Children = append(platformNode.Children, userNode)
- }
- tree = append(tree, platformNode)
+ groupProjects[g] = append(groupProjects[g], proj)
}
+
+ // 构建分组 → icon 映射
+ groupIcon := make(map[string]string)
+ for _, g := range s.config.Groups {
+ groupIcon[g.Name] = g.Icon
+ }
+
+ var tree []TreeNode
+ // 先输出已定义的分组(保持顺序)
+ rendered := make(map[string]bool)
+ for _, g := range s.config.Groups {
+ projects, ok := groupProjects[g.Name]
+ if !ok {
+ projects = nil
+ }
+ node := TreeNode{
+ Key: "group:" + g.Name,
+ Label: g.Name,
+ Type: "group",
+ Icon: g.Icon,
+ }
+ for _, proj := range projects {
+ node.Children = append(node.Children, TreeNode{
+ Key: "project:" + proj.Path,
+ Label: proj.Name,
+ Type: "project",
+ Path: proj.Path,
+ })
+ }
+ tree = append(tree, node)
+ rendered[g.Name] = true
+ }
+
+ // 输出"未分组"或不在 groups 列表里的项目
+ for groupName, projects := range groupProjects {
+ if rendered[groupName] {
+ continue
+ }
+ node := TreeNode{
+ Key: "group:" + groupName,
+ Label: groupName,
+ Type: "group",
+ Icon: "folder",
+ }
+ for _, proj := range projects {
+ node.Children = append(node.Children, TreeNode{
+ Key: "project:" + proj.Path,
+ Label: proj.Name,
+ Type: "project",
+ Path: proj.Path,
+ })
+ }
+ tree = append(tree, node)
+ }
+
return tree
}
// --- 项目管理 ---
-// CloneProject 克隆远程仓库到本地目录,并添加到项目树
-func (s *AppService) CloneProject(platform, username, repoURL, parentDir, name string) error {
+// CloneProject 克隆远程仓库到本地目录,并添加到项目列表
+func (s *AppService) CloneProject(repoURL, parentDir, name, group string) error {
if repoURL == "" {
return fmt.Errorf("仓库地址不能为空")
}
@@ -109,198 +151,219 @@ func (s *AppService) CloneProject(platform, username, repoURL, parentDir, name s
return fmt.Errorf("项目名称不能为空")
}
- // 目标路径: parentDir/name
targetPath := parentDir + "/" + name
- // 执行 git clone
_, err := s.gitClient.Clone(repoURL, targetPath)
if err != nil {
return fmt.Errorf("克隆失败: %w", err)
}
- // 克隆成功后添加到项目树
- return s.AddProject(platform, username, name, targetPath)
+ return s.AddProject(name, targetPath, group)
}
-// AddProject 添加项目到指定平台/用户下
-func (s *AppService) AddProject(platform, username, name, path string) error {
- p, ok := s.config.Platforms[platform]
- if !ok {
- return fmt.Errorf("平台 %s 不存在", platform)
+// AddProject 添加项目
+func (s *AppService) AddProject(name, path, group string) error {
+ // 检查路径重复
+ for _, proj := range s.config.Projects {
+ if proj.Path == path {
+ return fmt.Errorf("项目路径 %s 已存在", path)
+ }
}
- for i, user := range p.Users {
- if user.Username == username {
- // 检查重复
- for _, proj := range user.Projects {
- if proj.Name == name {
- return fmt.Errorf("项目 %s 已存在", name)
- }
+ s.config.Projects = append(s.config.Projects, config.ProjectItem{
+ Name: name,
+ Path: path,
+ Group: group,
+ })
+ // 如果分组不存在,自动创建
+ if group != "" {
+ found := false
+ for _, g := range s.config.Groups {
+ if g.Name == group {
+ found = true
+ break
}
- s.config.Platforms[platform].Users[i].Projects = append(
- s.config.Platforms[platform].Users[i].Projects,
- config.Project{Name: name, Path: path},
- )
+ }
+ if !found {
+ s.config.Groups = append(s.config.Groups, config.Group{Name: group, Icon: "folder"})
+ }
+ }
+ return config.SaveConfig(s.config)
+}
+
+// RemoveProject 删除项目(按路径匹配)
+func (s *AppService) RemoveProject(path string) error {
+ for i, proj := range s.config.Projects {
+ if proj.Path == path {
+ s.config.Projects = append(s.config.Projects[:i], s.config.Projects[i+1:]...)
return config.SaveConfig(s.config)
}
}
- return fmt.Errorf("用户 %s 不存在于平台 %s", username, platform)
+ return fmt.Errorf("项目不存在: %s", path)
}
-// RemoveProject 从指定平台/用户下删除项目
-func (s *AppService) RemoveProject(platform, username, name string) error {
- p, ok := s.config.Platforms[platform]
- if !ok {
- return fmt.Errorf("平台 %s 不存在", platform)
- }
- for i, user := range p.Users {
- if user.Username == username {
- projects := user.Projects
- for j, proj := range projects {
- if proj.Name == name {
- s.config.Platforms[platform].Users[i].Projects = append(projects[:j], projects[j+1:]...)
- return config.SaveConfig(s.config)
+// MoveProjectToGroup 移动项目到指定分组
+func (s *AppService) MoveProjectToGroup(path, group string) error {
+ for i, proj := range s.config.Projects {
+ if proj.Path == path {
+ s.config.Projects[i].Group = group
+ // 自动创建分组
+ if group != "" {
+ found := false
+ for _, g := range s.config.Groups {
+ if g.Name == group {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.config.Groups = append(s.config.Groups, config.Group{Name: group, Icon: "folder"})
}
}
- return fmt.Errorf("项目 %s 不存在", name)
+ return config.SaveConfig(s.config)
}
}
- return fmt.Errorf("用户 %s 不存在于平台 %s", username, platform)
+ return fmt.Errorf("项目不存在: %s", path)
}
-// --- 平台管理 ---
+// --- 分组管理 ---
-// AddPlatform 添加新平台
-func (s *AppService) AddPlatform(name, baseURL string) error {
+// GetGroups 获取所有分组
+func (s *AppService) GetGroups() []config.Group {
+ return s.config.Groups
+}
+
+// AddGroup 添加分组
+func (s *AppService) AddGroup(name, icon string) error {
if name == "" {
- return fmt.Errorf("平台名称不能为空")
+ return fmt.Errorf("分组名称不能为空")
}
- if _, ok := s.config.Platforms[name]; ok {
- return fmt.Errorf("平台 %s 已存在", name)
+ for _, g := range s.config.Groups {
+ if g.Name == name {
+ return fmt.Errorf("分组 %s 已存在", name)
+ }
}
- s.config.Platforms[name] = config.Platform{
- BaseURL: baseURL,
- Users: []config.User{},
+ if icon == "" {
+ icon = "folder"
}
+ s.config.Groups = append(s.config.Groups, config.Group{Name: name, Icon: icon})
return config.SaveConfig(s.config)
}
-// UpdatePlatform 修改平台信息(base_url)
-func (s *AppService) UpdatePlatform(name, baseURL string) error {
- p, ok := s.config.Platforms[name]
- if !ok {
- return fmt.Errorf("平台 %s 不存在", name)
+// UpdateGroup 修改分组
+func (s *AppService) UpdateGroup(oldName, newName, icon string) error {
+ if newName == "" {
+ return fmt.Errorf("分组名称不能为空")
}
- p.BaseURL = baseURL
- s.config.Platforms[name] = p
- return config.SaveConfig(s.config)
+ for i, g := range s.config.Groups {
+ if g.Name == oldName {
+ // 如果改名,检查冲突并同步更新项目的分组引用
+ if oldName != newName {
+ for _, g2 := range s.config.Groups {
+ if g2.Name == newName {
+ return fmt.Errorf("分组 %s 已存在", newName)
+ }
+ }
+ for j, proj := range s.config.Projects {
+ if proj.Group == oldName {
+ s.config.Projects[j].Group = newName
+ }
+ }
+ }
+ s.config.Groups[i].Name = newName
+ if icon != "" {
+ s.config.Groups[i].Icon = icon
+ }
+ return config.SaveConfig(s.config)
+ }
+ }
+ return fmt.Errorf("分组 %s 不存在", oldName)
}
-// RemovePlatform 删除平台
-func (s *AppService) RemovePlatform(name string) error {
- if _, ok := s.config.Platforms[name]; !ok {
- return fmt.Errorf("平台 %s 不存在", name)
+// RemoveGroup 删除分组(分组下的项目变为"未分组")
+func (s *AppService) RemoveGroup(name string) error {
+ for i, g := range s.config.Groups {
+ if g.Name == name {
+ s.config.Groups = append(s.config.Groups[:i], s.config.Groups[i+1:]...)
+ // 将该分组下项目归入未分组
+ for j, proj := range s.config.Projects {
+ if proj.Group == name {
+ s.config.Projects[j].Group = ""
+ }
+ }
+ return config.SaveConfig(s.config)
+ }
}
- delete(s.config.Platforms, name)
- return config.SaveConfig(s.config)
+ return fmt.Errorf("分组 %s 不存在", name)
}
-// --- 用户管理 ---
+// --- 凭证管理 ---
-// AddUser 添加用户到指定平台
-func (s *AppService) AddUser(platform, username, token string) error {
- p, ok := s.config.Platforms[platform]
- if !ok {
- return fmt.Errorf("平台 %s 不存在", platform)
+// CredentialInfo 凭证信息(前端展示用)
+type CredentialInfo struct {
+ Platform string `json:"platform"`
+ BaseURL string `json:"baseUrl"`
+ Username string `json:"username"`
+ Token string `json:"token"`
+}
+
+// GetCredentials 获取所有凭证
+func (s *AppService) GetCredentials() []CredentialInfo {
+ var result []CredentialInfo
+ for _, c := range s.config.Credentials {
+ result = append(result, CredentialInfo{
+ Platform: c.Platform,
+ BaseURL: c.BaseURL,
+ Username: c.Username,
+ Token: c.Token,
+ })
+ }
+ return result
+}
+
+// AddCredential 添加凭证
+func (s *AppService) AddCredential(platform, baseURL, username, token string) error {
+ if platform == "" {
+ return fmt.Errorf("平台不能为空")
}
if username == "" {
return fmt.Errorf("用户名不能为空")
}
- for _, user := range p.Users {
- if user.Username == username {
- return fmt.Errorf("用户 %s 已存在于平台 %s", username, platform)
+ // 检查重复(同平台同用户名)
+ for _, c := range s.config.Credentials {
+ if c.Platform == platform && c.Username == username {
+ return fmt.Errorf("凭证已存在: %s/%s", platform, username)
}
}
- p.Users = append(p.Users, config.User{
+ s.config.Credentials = append(s.config.Credentials, config.Credential{
+ Platform: platform,
+ BaseURL: baseURL,
Username: username,
Token: token,
- Projects: []config.Project{},
})
- s.config.Platforms[platform] = p
return config.SaveConfig(s.config)
}
-// UpdateUser 修改用户信息(用户名、token)
-func (s *AppService) UpdateUser(platform, oldUsername, newUsername, token string) error {
- p, ok := s.config.Platforms[platform]
- if !ok {
- return fmt.Errorf("平台 %s 不存在", platform)
- }
- if newUsername == "" {
- return fmt.Errorf("新用户名不能为空")
- }
- for i, user := range p.Users {
- if user.Username == oldUsername {
- // 如果改了用户名,检查新名字是否冲突
- if oldUsername != newUsername {
- for _, u := range p.Users {
- if u.Username == newUsername {
- return fmt.Errorf("用户 %s 已存在于平台 %s", newUsername, platform)
- }
- }
- }
- s.config.Platforms[platform].Users[i].Username = newUsername
- s.config.Platforms[platform].Users[i].Token = token
+// UpdateCredential 更新凭证
+func (s *AppService) UpdateCredential(platform, username, baseURL, token string) error {
+ for i, c := range s.config.Credentials {
+ if c.Platform == platform && c.Username == username {
+ s.config.Credentials[i].BaseURL = baseURL
+ s.config.Credentials[i].Token = token
return config.SaveConfig(s.config)
}
}
- return fmt.Errorf("用户 %s 不存在于平台 %s", oldUsername, platform)
+ return fmt.Errorf("凭证不存在: %s/%s", platform, username)
}
-// RemoveUser 从平台删除用户
-func (s *AppService) RemoveUser(platform, username string) error {
- p, ok := s.config.Platforms[platform]
- if !ok {
- return fmt.Errorf("平台 %s 不存在", platform)
- }
- for i, user := range p.Users {
- if user.Username == username {
- s.config.Platforms[platform] = config.Platform{
- BaseURL: p.BaseURL,
- Users: append(p.Users[:i], p.Users[i+1:]...),
- }
+// RemoveCredential 删除凭证
+func (s *AppService) RemoveCredential(platform, username string) error {
+ for i, c := range s.config.Credentials {
+ if c.Platform == platform && c.Username == username {
+ s.config.Credentials = append(s.config.Credentials[:i], s.config.Credentials[i+1:]...)
return config.SaveConfig(s.config)
}
}
- return fmt.Errorf("用户 %s 不存在于平台 %s", username, platform)
-}
-
-// GetUserInfo 获取用户信息
-func (s *AppService) GetUserInfo(platform, username string) (*UserInfo, error) {
- p, ok := s.config.Platforms[platform]
- if !ok {
- return nil, fmt.Errorf("平台 %s 不存在", platform)
- }
- for _, user := range p.Users {
- if user.Username == username {
- return &UserInfo{
- Username: user.Username,
- Token: user.Token,
- }, nil
- }
- }
- return nil, fmt.Errorf("用户 %s 不存在于平台 %s", username, platform)
-}
-
-// GetPlatformInfo 获取平台信息
-func (s *AppService) GetPlatformInfo(name string) (*PlatformInfo, error) {
- p, ok := s.config.Platforms[name]
- if !ok {
- return nil, fmt.Errorf("平台 %s 不存在", name)
- }
- return &PlatformInfo{
- Name: name,
- BaseURL: p.BaseURL,
- }, nil
+ return fmt.Errorf("凭证不存在: %s/%s", platform, username)
}
// --- Git 操作 ---
@@ -312,6 +375,7 @@ type ProjectStatus struct {
Remotes []RemoteItem `json:"remotes"`
CurrentRemote string `json:"currentRemote"`
ChangedFiles []FileInfo `json:"changedFiles"`
+ UseProxy *bool `json:"useProxy"` // nil=跟随全局, true=开启, false=关闭
}
// RemoteItem 远程仓库信息
@@ -328,18 +392,6 @@ type FileInfo struct {
Staged bool `json:"staged"`
}
-// UserInfo 用户信息
-type UserInfo struct {
- Username string `json:"username"`
- Token string `json:"token"`
-}
-
-// PlatformInfo 平台信息
-type PlatformInfo struct {
- Name string `json:"name"`
- BaseURL string `json:"baseUrl"`
-}
-
// GetProjectStatus 获取项目 git 状态
func (s *AppService) GetProjectStatus(path string) (*ProjectStatus, error) {
// 先校验路径是否存在
@@ -367,12 +419,22 @@ func (s *AppService) GetProjectStatus(path string) (*ProjectStatus, error) {
currentRemote = remotes[0].Name
}
+ // 获取项目代理设置
+ var useProxy *bool
+ for _, proj := range s.config.Projects {
+ if proj.Path == path {
+ useProxy = proj.UseProxy
+ break
+ }
+ }
+
return &ProjectStatus{
Branch: strings.TrimSpace(branch),
RemoteURL: remoteURL,
Remotes: remotes,
CurrentRemote: currentRemote,
ChangedFiles: []FileInfo{},
+ UseProxy: useProxy,
}, nil
}
@@ -547,7 +609,8 @@ func (s *AppService) PullProject(path, remote string) (string, error) {
if err != nil {
return "", fmt.Errorf("获取当前分支失败: %w", err)
}
- return s.gitClient.PullFrom(path, remote, strings.TrimSpace(branch))
+ proxy := s.GetProjectProxy(path)
+ return s.gitClient.RunWithProxy(path, proxy, "pull", remote, strings.TrimSpace(branch))
}
// PushProject 推送项目(当前分支,指定 remote)
@@ -559,7 +622,102 @@ func (s *AppService) PushProject(path, remote string) (string, error) {
if err != nil {
return "", fmt.Errorf("获取当前分支失败: %w", err)
}
- return s.gitClient.PushTo(path, remote, strings.TrimSpace(branch))
+ proxy := s.GetProjectProxy(path)
+ return s.gitClient.RunWithProxy(path, proxy, "push", remote, strings.TrimSpace(branch))
+}
+
+// PushAllResult 一键多端推送结果
+type PushAllResult struct {
+ Remote string `json:"remote"`
+ Success bool `json:"success"`
+ Message string `json:"message"`
+}
+
+// PushToAllRemotes 一键推送到所有远程仓库(当前分支)
+func (s *AppService) PushToAllRemotes(path string) ([]PushAllResult, error) {
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ return nil, fmt.Errorf("项目路径不存在: %s", path)
+ }
+ branch, err := s.gitClient.Branch(path)
+ if err != nil {
+ return nil, fmt.Errorf("获取当前分支失败: %w", err)
+ }
+ branch = strings.TrimSpace(branch)
+
+ remoteList, err := s.gitClient.RemoteList(path)
+ if err != nil {
+ return nil, fmt.Errorf("获取远程仓库列表失败: %w", err)
+ }
+
+ proxy := s.GetProjectProxy(path)
+ var results []PushAllResult
+ for _, r := range remoteList {
+ result := PushAllResult{Remote: r.Name}
+ _, pushErr := s.gitClient.RunWithProxy(path, proxy, "push", r.Name, branch)
+ if pushErr != nil {
+ result.Message = pushErr.Error()
+ } else {
+ result.Success = true
+ result.Message = "推送成功"
+ }
+ results = append(results, result)
+ }
+ return results, nil
+}
+
+// PushTagToAllRemotes 一键推送标签到所有远程仓库
+func (s *AppService) PushTagToAllRemotes(path, tagName string) ([]PushAllResult, error) {
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ return nil, fmt.Errorf("项目路径不存在: %s", path)
+ }
+ tagName = strings.TrimSpace(tagName)
+ if tagName == "" {
+ return nil, fmt.Errorf("标签名不能为空")
+ }
+
+ remoteList, err := s.gitClient.RemoteList(path)
+ if err != nil {
+ return nil, fmt.Errorf("获取远程仓库列表失败: %w", err)
+ }
+
+ proxy := s.GetProjectProxy(path)
+ var results []PushAllResult
+ for _, r := range remoteList {
+ result := PushAllResult{Remote: r.Name}
+ _, pushErr := s.gitClient.RunWithProxy(path, proxy, "push", r.Name, tagName)
+ if pushErr != nil {
+ result.Message = pushErr.Error()
+ } else {
+ result.Success = true
+ result.Message = "推送成功"
+ }
+ results = append(results, result)
+ }
+ return results, nil
+}
+
+// --- 项目代理开关 ---
+
+// GetProjectProxy 获取项目代理设置(nil 表示跟随全局)
+func (s *AppService) GetProjectProxy(path string) *bool {
+ for _, proj := range s.config.Projects {
+ if proj.Path == path {
+ return proj.UseProxy
+ }
+ }
+ return nil
+}
+
+// SetProjectProxy 设置项目代理开关
+// useProxy: true=强制启用, false=强制禁用, nil(传空)=跟随全局
+func (s *AppService) SetProjectProxy(path string, useProxy *bool) error {
+ for i, proj := range s.config.Projects {
+ if proj.Path == path {
+ s.config.Projects[i].UseProxy = useProxy
+ return config.SaveConfig(s.config)
+ }
+ }
+ return fmt.Errorf("项目不存在: %s", path)
}
// GetCommitDiff 获取指定提交的 diff
@@ -612,10 +770,11 @@ func (s *AppService) GetCommitFileDiff(path, hash, filePath string) (string, err
// FetchProject 拉取远程信息(指定 remote,空则 fetch --all)
func (s *AppService) FetchProject(path, remote string) (string, error) {
+ proxy := s.GetProjectProxy(path)
if remote == "" {
- return s.gitClient.Fetch(path)
+ return s.gitClient.RunWithProxy(path, proxy, "fetch")
}
- return s.gitClient.FetchRemote(path, remote)
+ return s.gitClient.RunWithProxy(path, proxy, "fetch", remote)
}
// CommitLog 提交记录
@@ -1269,30 +1428,26 @@ type ProjectOverview struct {
// GetAllProjectOverview 获取所有项目的概览状态
func (s *AppService) GetAllProjectOverview() []ProjectOverview {
var results []ProjectOverview
- for platformName, platform := range s.config.Platforms {
- for _, user := range platform.Users {
- for _, proj := range user.Projects {
- overview := ProjectOverview{
- Key: platformName + "/" + user.Username + "/" + proj.Name,
- Name: proj.Name,
- Path: proj.Path,
- }
- if _, err := os.Stat(proj.Path); os.IsNotExist(err) {
- overview.Error = "路径不存在"
- results = append(results, overview)
- continue
- }
- branch, hasChanges, unpushed, err := s.gitClient.QuickStatus(proj.Path)
- if err != nil {
- overview.Error = err.Error()
- } else {
- overview.Branch = branch
- overview.HasChanges = hasChanges
- overview.Unpushed = unpushed
- }
- results = append(results, overview)
- }
+ for _, proj := range s.config.Projects {
+ overview := ProjectOverview{
+ Key: proj.Path,
+ Name: proj.Name,
+ Path: proj.Path,
}
+ if _, err := os.Stat(proj.Path); os.IsNotExist(err) {
+ overview.Error = "路径不存在"
+ results = append(results, overview)
+ continue
+ }
+ branch, hasChanges, unpushed, err := s.gitClient.QuickStatus(proj.Path)
+ if err != nil {
+ overview.Error = err.Error()
+ } else {
+ overview.Branch = branch
+ overview.HasChanges = hasChanges
+ overview.Unpushed = unpushed
+ }
+ results = append(results, overview)
}
return results
}
diff --git a/internal/git/client.go b/internal/git/client.go
index a445b76..805f95b 100644
--- a/internal/git/client.go
+++ b/internal/git/client.go
@@ -29,10 +29,20 @@ func NewGitClient() *GitClient {
// Run 执行 git 命令,支持超时和代理设置
func (g *GitClient) Run(path string, args ...string) (string, error) {
+ return g.RunWithProxy(path, nil, args...)
+}
+
+// RunWithProxy 执行 git 命令,useProxy 可覆盖全局代理设置
+// useProxy == nil 时跟随 GitClient 自身的 Enabled 设置
+func (g *GitClient) RunWithProxy(path string, useProxy *bool, args ...string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), g.Timeout)
defer cancel()
header := []string{"-C", path, "-c", "core.quotePath=false"}
- if g.Enabled {
+ enableProxy := g.Enabled
+ if useProxy != nil {
+ enableProxy = *useProxy
+ }
+ if enableProxy {
header = append(header, "-c", "http.proxy="+g.Proxy, "-c", "https.proxy="+g.Proxy)
}
argsArr := append(header, args...)