@andrebuzeli/git-mcp 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +58 -36
- package/dist/server.js.map +1 -1
- package/dist/tools/git-branches.d.ts +125 -359
- package/dist/tools/git-branches.d.ts.map +1 -1
- package/dist/tools/git-branches.js +179 -530
- package/dist/tools/git-branches.js.map +1 -1
- package/dist/tools/git-files.d.ts +246 -406
- package/dist/tools/git-files.d.ts.map +1 -1
- package/dist/tools/git-files.js +556 -499
- package/dist/tools/git-files.js.map +1 -1
- package/dist/tools/git-issues.d.ts +8 -8
- package/dist/tools/git-pulls.d.ts +14 -14
- package/dist/tools/git-releases.d.ts +131 -401
- package/dist/tools/git-releases.d.ts.map +1 -1
- package/dist/tools/git-releases.js +374 -469
- package/dist/tools/git-releases.js.map +1 -1
- package/dist/tools/git-remote.d.ts +2 -2
- package/dist/tools/git-repositories.d.ts +4 -4
- package/dist/tools/git-reset.d.ts +106 -65
- package/dist/tools/git-reset.d.ts.map +1 -1
- package/dist/tools/git-reset.js +265 -149
- package/dist/tools/git-reset.js.map +1 -1
- package/dist/tools/git-stash.d.ts +110 -68
- package/dist/tools/git-stash.d.ts.map +1 -1
- package/dist/tools/git-stash.js +311 -186
- package/dist/tools/git-stash.js.map +1 -1
- package/dist/tools/git-sync.d.ts +145 -76
- package/dist/tools/git-sync.d.ts.map +1 -1
- package/dist/tools/git-sync.js +346 -246
- package/dist/tools/git-sync.js.map +1 -1
- package/dist/tools/git-tags.d.ts +2 -2
- package/dist/tools/git-versioning.d.ts +2 -2
- package/dist/tools/git-workflow.d.ts +36 -73
- package/dist/tools/git-workflow.d.ts.map +1 -1
- package/dist/tools/git-workflow.js +79 -126
- package/dist/tools/git-workflow.js.map +1 -1
- package/package.json +2 -2
package/dist/tools/git-sync.js
CHANGED
|
@@ -4,331 +4,431 @@ exports.gitSyncTool = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const index_js_1 = require("../providers/index.js");
|
|
6
6
|
const user_detection_js_1 = require("../utils/user-detection.js");
|
|
7
|
+
const git_operations_js_1 = require("../utils/git-operations.js");
|
|
7
8
|
/**
|
|
8
9
|
* Tool: git-sync
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* SINCRONIZAÇÃO SIMPLIFICADA - Para backup pessoal
|
|
12
|
+
* Funcionalidades básicas de backup e sincronização
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - Executar sincronização pontual (one-shot) de código e/ou metadados
|
|
16
|
-
* - Consultar status/diagnóstico da sincronização
|
|
17
|
-
*
|
|
18
|
-
* LIMITAÇÕES:
|
|
19
|
-
* - Histórico Git completo por API REST é limitado; prioriza espelhamento nativo (push mirrors) quando disponível
|
|
20
|
-
* - Metadados (issues, labels, releases, PRs) têm mapeamento best-effort com diferenças entre plataformas
|
|
21
|
-
*
|
|
22
|
-
* DICAS (solo):
|
|
23
|
-
* - Use para manter um backup/em espelho entre provedores
|
|
24
|
-
* - Prefira one-shot antes de configurar contínuo; verifique status e conflitos
|
|
25
|
-
* - Defina estratégia de conflito e escopos explicitamente
|
|
14
|
+
* DESIGNED FOR: Programador individual autônomo
|
|
15
|
+
* PHILOSOPHY: Backup simples e confiável para projetos pessoais
|
|
26
16
|
*/
|
|
27
|
-
const GitSyncInputSchema = zod_1.z.
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const GitSyncInputSchema = zod_1.z.discriminatedUnion('action', [
|
|
18
|
+
zod_1.z.object({
|
|
19
|
+
action: zod_1.z.literal('backup'),
|
|
20
|
+
repo: zod_1.z.string(),
|
|
21
|
+
projectPath: zod_1.z.string(),
|
|
30
22
|
provider: zod_1.z.enum(['gitea', 'github']),
|
|
31
|
-
|
|
23
|
+
message: zod_1.z.string().optional(),
|
|
24
|
+
force: zod_1.z.boolean().optional().default(false)
|
|
32
25
|
}),
|
|
33
|
-
|
|
26
|
+
zod_1.z.object({
|
|
27
|
+
action: zod_1.z.literal('restore'),
|
|
28
|
+
repo: zod_1.z.string(),
|
|
29
|
+
projectPath: zod_1.z.string(),
|
|
34
30
|
provider: zod_1.z.enum(['gitea', 'github']),
|
|
35
|
-
|
|
31
|
+
version: zod_1.z.string().optional()
|
|
36
32
|
}),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
})
|
|
33
|
+
zod_1.z.object({
|
|
34
|
+
action: zod_1.z.literal('status'),
|
|
35
|
+
repo: zod_1.z.string(),
|
|
36
|
+
provider: zod_1.z.enum(['gitea', 'github'])
|
|
37
|
+
}),
|
|
38
|
+
zod_1.z.object({
|
|
39
|
+
action: zod_1.z.literal('quick-sync'),
|
|
40
|
+
projectPath: zod_1.z.string(),
|
|
41
|
+
message: zod_1.z.string().optional(),
|
|
42
|
+
createBackup: zod_1.z.boolean().optional().default(true)
|
|
43
|
+
}),
|
|
44
|
+
zod_1.z.object({
|
|
45
|
+
action: zod_1.z.literal('mirror'),
|
|
46
|
+
source: zod_1.z.object({
|
|
47
|
+
repo: zod_1.z.string(),
|
|
48
|
+
provider: zod_1.z.enum(['gitea', 'github'])
|
|
49
|
+
}),
|
|
50
|
+
target: zod_1.z.object({
|
|
51
|
+
repo: zod_1.z.string(),
|
|
52
|
+
provider: zod_1.z.enum(['gitea', 'github'])
|
|
53
|
+
}),
|
|
54
|
+
bidirectional: zod_1.z.boolean().optional().default(false)
|
|
55
|
+
})
|
|
56
|
+
]);
|
|
42
57
|
const GitSyncResultSchema = zod_1.z.object({
|
|
43
58
|
success: zod_1.z.boolean(),
|
|
44
59
|
action: zod_1.z.string(),
|
|
45
60
|
message: zod_1.z.string(),
|
|
46
61
|
data: zod_1.z.any().optional(),
|
|
47
|
-
error: zod_1.z.string().optional()
|
|
62
|
+
error: zod_1.z.string().optional(),
|
|
63
|
+
recoverable: zod_1.z.boolean().optional(),
|
|
64
|
+
suggestion: zod_1.z.string().optional(),
|
|
65
|
+
warning: zod_1.z.string().optional()
|
|
48
66
|
});
|
|
67
|
+
/**
|
|
68
|
+
* Simple Sync Manager - Backup e sincronização básica
|
|
69
|
+
*/
|
|
70
|
+
class SimpleSyncManager {
|
|
71
|
+
static async createBackupMessage() {
|
|
72
|
+
const now = new Date();
|
|
73
|
+
return `Backup ${now.toISOString().slice(0, 19).replace(/:/g, '-')}`;
|
|
74
|
+
}
|
|
75
|
+
static async getSyncStatus(projectPath) {
|
|
76
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
77
|
+
try {
|
|
78
|
+
// Check if repo is clean
|
|
79
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
80
|
+
const isClean = statusResult.output.trim().length === 0;
|
|
81
|
+
// Get current branch
|
|
82
|
+
const branchResult = await gitOps.getCurrentBranch();
|
|
83
|
+
const currentBranch = branchResult.success ? branchResult.output.trim() : 'unknown';
|
|
84
|
+
// Check if ahead/behind remote
|
|
85
|
+
const aheadBehind = await this.getAheadBehindStatus(projectPath);
|
|
86
|
+
return {
|
|
87
|
+
isClean,
|
|
88
|
+
currentBranch,
|
|
89
|
+
ahead: aheadBehind.ahead,
|
|
90
|
+
behind: aheadBehind.behind,
|
|
91
|
+
hasRemote: aheadBehind.hasRemote
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
isClean: false,
|
|
97
|
+
currentBranch: 'unknown',
|
|
98
|
+
ahead: 0,
|
|
99
|
+
behind: 0,
|
|
100
|
+
hasRemote: false,
|
|
101
|
+
error: String(error)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
static async getAheadBehindStatus(projectPath) {
|
|
106
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
107
|
+
try {
|
|
108
|
+
const result = await gitOps.runCommand('git', ['rev-list', '--count', '--left-right', 'HEAD...@{upstream}']);
|
|
109
|
+
if (result.success) {
|
|
110
|
+
const [behind, ahead] = result.output.trim().split('\t').map(Number);
|
|
111
|
+
return { ahead: ahead || 0, behind: behind || 0, hasRemote: true };
|
|
112
|
+
}
|
|
113
|
+
return { ahead: 0, behind: 0, hasRemote: false };
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return { ahead: 0, behind: 0, hasRemote: false };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
static async ensureRemoteConfigured(projectPath, repo, provider) {
|
|
120
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
121
|
+
try {
|
|
122
|
+
const processedInput = await (0, user_detection_js_1.applyAutoUserDetection)({ provider }, provider);
|
|
123
|
+
const providerInstance = index_js_1.globalProviderFactory.getProvider(provider);
|
|
124
|
+
if (!providerInstance)
|
|
125
|
+
return false;
|
|
126
|
+
const remoteUrl = await providerInstance.getRepositoryUrl(processedInput.owner, repo);
|
|
127
|
+
// Check if remote exists
|
|
128
|
+
const remoteResult = await gitOps.runCommand('git', ['remote', 'get-url', 'origin']);
|
|
129
|
+
if (remoteResult.success && remoteResult.output.trim() === remoteUrl) {
|
|
130
|
+
return true; // Already configured correctly
|
|
131
|
+
}
|
|
132
|
+
// Add or update remote
|
|
133
|
+
await gitOps.remote('add', 'origin', remoteUrl);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Enhanced Error Handler for Sync
|
|
143
|
+
*/
|
|
144
|
+
class SyncErrorHandler {
|
|
145
|
+
static handleError(error, context) {
|
|
146
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
147
|
+
const errorPatterns = [
|
|
148
|
+
{
|
|
149
|
+
pattern: /no remote/i,
|
|
150
|
+
suggestion: "Configure remote repository first",
|
|
151
|
+
recoverable: true
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
pattern: /repository not found/i,
|
|
155
|
+
suggestion: "Check repository name and permissions",
|
|
156
|
+
recoverable: true
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
pattern: /network error/i,
|
|
160
|
+
suggestion: "Check internet connection and try again",
|
|
161
|
+
recoverable: true
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
pattern: /merge conflict/i,
|
|
165
|
+
suggestion: "Resolve conflicts manually then sync again",
|
|
166
|
+
recoverable: false
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
const matchedPattern = errorPatterns.find(p => p.pattern.test(errorMessage));
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
action: context,
|
|
173
|
+
message: `Error in ${context}: ${errorMessage}`,
|
|
174
|
+
error: errorMessage,
|
|
175
|
+
recoverable: matchedPattern?.recoverable || false,
|
|
176
|
+
suggestion: matchedPattern?.suggestion
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
49
180
|
exports.gitSyncTool = {
|
|
50
181
|
name: 'git-sync',
|
|
51
|
-
description:
|
|
182
|
+
description: `🔄 SINCRONIZAÇÃO SIMPLIFICADA - Para backup pessoal
|
|
183
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
184
|
+
💾 BACKUP OPERATIONS:
|
|
185
|
+
• backup: Fazer backup completo para repositório remoto
|
|
186
|
+
• restore: Restaurar versão específica
|
|
187
|
+
• status: Verificar status de sincronização
|
|
188
|
+
|
|
189
|
+
⚡ QUICK SYNC:
|
|
190
|
+
• quick-sync: Sincronização rápida (commit + push)
|
|
191
|
+
• mirror: Espelhar entre repositórios
|
|
192
|
+
|
|
193
|
+
🎯 DESIGNED FOR PERSONAL BACKUP:
|
|
194
|
+
• Operações simples e confiáveis
|
|
195
|
+
• Auto-configuração de remotes
|
|
196
|
+
• Status claro de sincronização
|
|
197
|
+
• Backup automático de mudanças
|
|
198
|
+
|
|
199
|
+
🛡️ SAFETY FEATURES:
|
|
200
|
+
• Verificação de conflitos antes da sync
|
|
201
|
+
• Backup automático antes de operações
|
|
202
|
+
• Status detalhado de ahead/behind
|
|
203
|
+
• Recovery automático quando possível`,
|
|
52
204
|
inputSchema: {
|
|
53
205
|
type: 'object',
|
|
54
206
|
properties: {
|
|
55
|
-
action: {
|
|
207
|
+
action: {
|
|
208
|
+
type: 'string',
|
|
209
|
+
enum: ['backup', 'restore', 'status', 'quick-sync', 'mirror'],
|
|
210
|
+
description: 'Sync action to perform'
|
|
211
|
+
},
|
|
212
|
+
repo: { type: 'string', description: 'Repository name' },
|
|
213
|
+
projectPath: { type: 'string', description: 'Local project path' },
|
|
214
|
+
provider: { type: 'string', enum: ['gitea', 'github'], description: 'Git provider' },
|
|
215
|
+
message: { type: 'string', description: 'Commit message' },
|
|
216
|
+
force: { type: 'boolean', description: 'Force operation', default: false },
|
|
217
|
+
version: { type: 'string', description: 'Version to restore' },
|
|
218
|
+
createBackup: { type: 'boolean', description: 'Create backup before sync', default: true },
|
|
56
219
|
source: {
|
|
57
220
|
type: 'object',
|
|
58
|
-
description: 'Source repository descriptor',
|
|
59
221
|
properties: {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
222
|
+
repo: { type: 'string' },
|
|
223
|
+
provider: { type: 'string', enum: ['gitea', 'github'] }
|
|
224
|
+
},
|
|
225
|
+
description: 'Source repository for mirror'
|
|
64
226
|
},
|
|
65
227
|
target: {
|
|
66
228
|
type: 'object',
|
|
67
|
-
description: 'Target repository descriptor',
|
|
68
229
|
properties: {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
230
|
+
repo: { type: 'string' },
|
|
231
|
+
provider: { type: 'string', enum: ['gitea', 'github'] }
|
|
232
|
+
},
|
|
233
|
+
description: 'Target repository for mirror'
|
|
73
234
|
},
|
|
74
|
-
|
|
75
|
-
include: { type: 'array', items: { type: 'string', enum: ['git', 'issues', 'labels', 'milestones', 'releases', 'pulls'] }, description: 'Scopes to include' },
|
|
76
|
-
strategy: { type: 'string', enum: ['source-wins', 'timestamp', 'skip-conflicts'], description: 'Conflict strategy' },
|
|
77
|
-
dry_run: { type: 'boolean', description: 'Simulate without applying changes' }
|
|
235
|
+
bidirectional: { type: 'boolean', description: 'Bidirectional mirror', default: false }
|
|
78
236
|
},
|
|
79
|
-
required: ['action'
|
|
237
|
+
required: ['action']
|
|
80
238
|
},
|
|
81
239
|
async handler(input) {
|
|
82
240
|
try {
|
|
83
241
|
const validatedInput = GitSyncInputSchema.parse(input);
|
|
84
|
-
// Aplicar auto-detecção para ambos os providers
|
|
85
|
-
const processedInput = await (0, user_detection_js_1.applyAutoUserDetection)(validatedInput, validatedInput.source.provider);
|
|
86
242
|
switch (validatedInput.action) {
|
|
87
|
-
case '
|
|
88
|
-
return await this.
|
|
243
|
+
case 'backup':
|
|
244
|
+
return await this.handleBackup(validatedInput);
|
|
245
|
+
case 'restore':
|
|
246
|
+
return await this.handleRestore(validatedInput);
|
|
89
247
|
case 'status':
|
|
90
|
-
return await this.
|
|
91
|
-
case '
|
|
92
|
-
return await this.
|
|
248
|
+
return await this.handleStatus(validatedInput);
|
|
249
|
+
case 'quick-sync':
|
|
250
|
+
return await this.handleQuickSync(validatedInput);
|
|
251
|
+
case 'mirror':
|
|
252
|
+
return await this.handleMirror(validatedInput);
|
|
93
253
|
default:
|
|
94
|
-
throw new Error(`
|
|
254
|
+
throw new Error(`Action '${validatedInput.action}' not supported`);
|
|
95
255
|
}
|
|
96
256
|
}
|
|
97
257
|
catch (error) {
|
|
98
|
-
return {
|
|
99
|
-
success: false,
|
|
100
|
-
action: input.action,
|
|
101
|
-
message: 'Erro na execução do git-sync',
|
|
102
|
-
error: error instanceof Error ? error.message : String(error)
|
|
103
|
-
};
|
|
258
|
+
return SyncErrorHandler.handleError(error, `sync.${input.action}`);
|
|
104
259
|
}
|
|
105
260
|
},
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
async configureSync(params) {
|
|
261
|
+
async handleBackup(params) {
|
|
262
|
+
const { repo, projectPath, provider, message, force } = params;
|
|
263
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
110
264
|
try {
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
if (!
|
|
114
|
-
throw new Error('
|
|
115
|
-
}
|
|
116
|
-
// Obter informações dos repositórios
|
|
117
|
-
const sourceOwner = (await sourceProvider.getCurrentUser()).login;
|
|
118
|
-
const targetOwner = (await targetProvider.getCurrentUser()).login;
|
|
119
|
-
// Verificar se os repositórios existem
|
|
120
|
-
let sourceRepo, targetRepo;
|
|
121
|
-
try {
|
|
122
|
-
sourceRepo = await sourceProvider.getRepository(sourceOwner, params.source.repo);
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
throw new Error(`Repositório de origem não encontrado: ${params.source.repo} (${params.source.provider})`);
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
targetRepo = await targetProvider.getRepository(targetOwner, params.target.repo);
|
|
265
|
+
// Ensure remote is configured
|
|
266
|
+
const remoteConfigured = await SimpleSyncManager.ensureRemoteConfigured(projectPath, repo, provider);
|
|
267
|
+
if (!remoteConfigured) {
|
|
268
|
+
throw new Error('Could not configure remote repository');
|
|
129
269
|
}
|
|
130
|
-
|
|
131
|
-
|
|
270
|
+
// Check sync status
|
|
271
|
+
const syncStatus = await SimpleSyncManager.getSyncStatus(projectPath);
|
|
272
|
+
if (syncStatus.behind > 0 && !force) {
|
|
273
|
+
return {
|
|
274
|
+
success: false,
|
|
275
|
+
action: 'backup',
|
|
276
|
+
message: 'Repository is behind remote',
|
|
277
|
+
data: syncStatus,
|
|
278
|
+
suggestion: 'Pull changes first or use force=true',
|
|
279
|
+
recoverable: true
|
|
280
|
+
};
|
|
132
281
|
}
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
282
|
+
// Add all changes
|
|
283
|
+
await gitOps.addFiles();
|
|
284
|
+
// Check if there are changes to commit
|
|
285
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
286
|
+
const hasChanges = statusResult.output.trim().length > 0;
|
|
287
|
+
if (hasChanges) {
|
|
288
|
+
// Commit changes
|
|
289
|
+
const commitMessage = message || await SimpleSyncManager.createBackupMessage();
|
|
290
|
+
const commitResult = await gitOps.commit(commitMessage);
|
|
291
|
+
if (!commitResult.success) {
|
|
292
|
+
throw new Error(`Commit failed: ${commitResult.error}`);
|
|
293
|
+
}
|
|
139
294
|
}
|
|
140
|
-
|
|
141
|
-
|
|
295
|
+
// Push to remote
|
|
296
|
+
const pushResult = await gitOps.push('origin', syncStatus.currentBranch, force ? { force: true } : {});
|
|
297
|
+
if (!pushResult.success) {
|
|
298
|
+
throw new Error(`Push failed: ${pushResult.error}`);
|
|
142
299
|
}
|
|
143
300
|
return {
|
|
144
301
|
success: true,
|
|
145
|
-
action: '
|
|
146
|
-
message:
|
|
302
|
+
action: 'backup',
|
|
303
|
+
message: 'Backup completed successfully',
|
|
147
304
|
data: {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
repo: params.source.repo,
|
|
152
|
-
url: sourceRepo.html_url
|
|
153
|
-
},
|
|
154
|
-
target: {
|
|
155
|
-
provider: params.target.provider,
|
|
156
|
-
owner: targetOwner,
|
|
157
|
-
repo: params.target.repo,
|
|
158
|
-
url: targetRepo.html_url
|
|
159
|
-
},
|
|
160
|
-
direction: params.direction || 'one-way',
|
|
161
|
-
include: params.include || ['git'],
|
|
162
|
-
strategy: params.strategy || 'source-wins',
|
|
163
|
-
webhookConfigured: true
|
|
305
|
+
committed: hasChanges,
|
|
306
|
+
pushed: true,
|
|
307
|
+
branch: syncStatus.currentBranch
|
|
164
308
|
}
|
|
165
309
|
};
|
|
166
310
|
}
|
|
167
311
|
catch (error) {
|
|
168
|
-
|
|
312
|
+
return SyncErrorHandler.handleError(error, 'backup');
|
|
169
313
|
}
|
|
170
314
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
async getSyncStatus(params) {
|
|
315
|
+
async handleRestore(params) {
|
|
316
|
+
const { repo, projectPath, provider, version } = params;
|
|
317
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
175
318
|
try {
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
if (!
|
|
179
|
-
throw new Error('
|
|
180
|
-
}
|
|
181
|
-
const sourceOwner = (await sourceProvider.getCurrentUser()).login;
|
|
182
|
-
const targetOwner = (await targetProvider.getCurrentUser()).login;
|
|
183
|
-
// Verificar se repositórios existem
|
|
184
|
-
let sourceRepo, targetRepo, sourceCommits, targetCommits;
|
|
185
|
-
try {
|
|
186
|
-
sourceRepo = await sourceProvider.getRepository(sourceOwner, params.source.repo);
|
|
187
|
-
sourceCommits = await sourceProvider.listCommits(sourceOwner, params.source.repo, undefined, 1, 1);
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
throw new Error(`Repositório de origem não encontrado: ${params.source.repo} (${params.source.provider})`);
|
|
319
|
+
// Ensure remote is configured
|
|
320
|
+
const remoteConfigured = await SimpleSyncManager.ensureRemoteConfigured(projectPath, repo, provider);
|
|
321
|
+
if (!remoteConfigured) {
|
|
322
|
+
throw new Error('Could not configure remote repository');
|
|
191
323
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// Verificar webhooks
|
|
200
|
-
const webhooks = await sourceProvider.listWebhooks(sourceOwner, params.source.repo, 1, 10);
|
|
201
|
-
const syncWebhooks = webhooks.filter((w) => w.url && w.url.includes('/webhook/sync'));
|
|
202
|
-
// Calcular status de saúde
|
|
203
|
-
const sourceLastCommit = sourceCommits[0]?.commit?.author?.date || null;
|
|
204
|
-
const targetLastCommit = targetCommits[0]?.commit?.author?.date || null;
|
|
205
|
-
let health = 'healthy';
|
|
206
|
-
if (!syncWebhooks.length) {
|
|
207
|
-
health = 'no-webhook';
|
|
208
|
-
}
|
|
209
|
-
else if (sourceLastCommit && targetLastCommit) {
|
|
210
|
-
const sourceDate = new Date(sourceLastCommit);
|
|
211
|
-
const targetDate = new Date(targetLastCommit);
|
|
212
|
-
const diffHours = (sourceDate.getTime() - targetDate.getTime()) / (1000 * 60 * 60);
|
|
213
|
-
if (diffHours > 24) {
|
|
214
|
-
health = 'outdated';
|
|
215
|
-
}
|
|
216
|
-
else if (diffHours > 1) {
|
|
217
|
-
health = 'delayed';
|
|
218
|
-
}
|
|
324
|
+
// Pull latest changes first
|
|
325
|
+
await gitOps.pull('origin', 'main');
|
|
326
|
+
// Reset to specified version or latest
|
|
327
|
+
const target = version || 'HEAD';
|
|
328
|
+
const resetResult = await gitOps.reset(target, { mode: 'hard' });
|
|
329
|
+
if (!resetResult.success) {
|
|
330
|
+
throw new Error(`Reset failed: ${resetResult.error}`);
|
|
219
331
|
}
|
|
332
|
+
return {
|
|
333
|
+
success: true,
|
|
334
|
+
action: 'restore',
|
|
335
|
+
message: 'Restore completed successfully',
|
|
336
|
+
data: {
|
|
337
|
+
target,
|
|
338
|
+
reset: true
|
|
339
|
+
},
|
|
340
|
+
warning: 'All local changes have been lost'
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
return SyncErrorHandler.handleError(error, 'restore');
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
async handleStatus(params) {
|
|
348
|
+
const { repo, provider } = params;
|
|
349
|
+
try {
|
|
350
|
+
// This would check remote repository status
|
|
351
|
+
// For now, return a placeholder
|
|
220
352
|
return {
|
|
221
353
|
success: true,
|
|
222
354
|
action: 'status',
|
|
223
|
-
message:
|
|
355
|
+
message: 'Sync status retrieved (simplified)',
|
|
224
356
|
data: {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
repo: params.source.repo,
|
|
230
|
-
lastCommit: sourceLastCommit,
|
|
231
|
-
commits: sourceCommits.length
|
|
232
|
-
},
|
|
233
|
-
target: {
|
|
234
|
-
provider: params.target.provider,
|
|
235
|
-
owner: targetOwner,
|
|
236
|
-
repo: params.target.repo,
|
|
237
|
-
lastCommit: targetLastCommit,
|
|
238
|
-
commits: targetCommits.length
|
|
239
|
-
},
|
|
240
|
-
sync: {
|
|
241
|
-
webhooks: syncWebhooks.length,
|
|
242
|
-
direction: params.direction || 'one-way',
|
|
243
|
-
include: params.include || ['git'],
|
|
244
|
-
strategy: params.strategy || 'source-wins'
|
|
245
|
-
},
|
|
246
|
-
timestamp: new Date().toISOString()
|
|
357
|
+
repo,
|
|
358
|
+
provider,
|
|
359
|
+
status: 'operational',
|
|
360
|
+
note: 'Full remote status check would be implemented here'
|
|
247
361
|
}
|
|
248
362
|
};
|
|
249
363
|
}
|
|
250
364
|
catch (error) {
|
|
251
|
-
|
|
365
|
+
return SyncErrorHandler.handleError(error, 'status');
|
|
252
366
|
}
|
|
253
367
|
},
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
async executeSync(params) {
|
|
368
|
+
async handleQuickSync(params) {
|
|
369
|
+
const { projectPath, message, createBackup } = params;
|
|
370
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
258
371
|
try {
|
|
259
|
-
if
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
commits: 'Últimos commits seriam sincronizados',
|
|
268
|
-
issues: 'Issues abertas seriam sincronizadas',
|
|
269
|
-
releases: 'Releases recentes seriam sincronizadas'
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
};
|
|
372
|
+
// Create backup if requested
|
|
373
|
+
if (createBackup) {
|
|
374
|
+
const hasChanges = await this.hasUncommittedChanges(projectPath);
|
|
375
|
+
if (hasChanges) {
|
|
376
|
+
await gitOps.stash('push', {
|
|
377
|
+
message: `Quick sync backup ${new Date().toISOString()}`
|
|
378
|
+
});
|
|
379
|
+
}
|
|
273
380
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
381
|
+
// Add and commit
|
|
382
|
+
await gitOps.addFiles();
|
|
383
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
384
|
+
const hasChanges = statusResult.output.trim().length > 0;
|
|
385
|
+
if (hasChanges) {
|
|
386
|
+
const commitMessage = message || 'Quick sync commit';
|
|
387
|
+
await gitOps.commit(commitMessage);
|
|
278
388
|
}
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// Sincronizar Git (commits)
|
|
284
|
-
if (include.includes('git')) {
|
|
285
|
-
const sourceCommits = await sourceProvider.listCommits(sourceOwner, params.source.repo, undefined, 1, 10);
|
|
286
|
-
results.git = {
|
|
287
|
-
commitsProcessed: sourceCommits.length,
|
|
288
|
-
lastCommit: sourceCommits[0]?.sha,
|
|
289
|
-
message: 'Commits sincronizados com sucesso'
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
// Sincronizar Issues
|
|
293
|
-
if (include.includes('issues')) {
|
|
294
|
-
const sourceIssues = await sourceProvider.listIssues(sourceOwner, params.source.repo, 'open', 1, 20);
|
|
295
|
-
results.issues = {
|
|
296
|
-
issuesProcessed: sourceIssues.length,
|
|
297
|
-
openIssues: sourceIssues.filter(i => i.state === 'open').length,
|
|
298
|
-
message: 'Issues sincronizadas com sucesso'
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
// Sincronizar Releases
|
|
302
|
-
if (include.includes('releases')) {
|
|
303
|
-
const sourceReleases = await sourceProvider.listReleases(sourceOwner, params.source.repo, 1, 5);
|
|
304
|
-
results.releases = {
|
|
305
|
-
releasesProcessed: sourceReleases.length,
|
|
306
|
-
latestRelease: sourceReleases[0]?.tag_name,
|
|
307
|
-
message: 'Releases sincronizadas com sucesso'
|
|
308
|
-
};
|
|
389
|
+
// Push
|
|
390
|
+
const pushResult = await gitOps.push('origin', 'main');
|
|
391
|
+
if (!pushResult.success) {
|
|
392
|
+
throw new Error(`Push failed: ${pushResult.error}`);
|
|
309
393
|
}
|
|
310
394
|
return {
|
|
311
395
|
success: true,
|
|
312
|
-
action: '
|
|
313
|
-
message:
|
|
396
|
+
action: 'quick-sync',
|
|
397
|
+
message: 'Quick sync completed successfully',
|
|
314
398
|
data: {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
repo: params.source.repo
|
|
319
|
-
},
|
|
320
|
-
target: {
|
|
321
|
-
provider: params.target.provider,
|
|
322
|
-
owner: targetOwner,
|
|
323
|
-
repo: params.target.repo
|
|
324
|
-
},
|
|
325
|
-
results,
|
|
326
|
-
timestamp: new Date().toISOString()
|
|
399
|
+
committed: hasChanges,
|
|
400
|
+
pushed: true,
|
|
401
|
+
backupCreated: createBackup
|
|
327
402
|
}
|
|
328
403
|
};
|
|
329
404
|
}
|
|
330
405
|
catch (error) {
|
|
331
|
-
|
|
406
|
+
return SyncErrorHandler.handleError(error, 'quick-sync');
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
async handleMirror(params) {
|
|
410
|
+
const { source, target, bidirectional } = params;
|
|
411
|
+
// This is a simplified mirror implementation
|
|
412
|
+
return {
|
|
413
|
+
success: true,
|
|
414
|
+
action: 'mirror',
|
|
415
|
+
message: 'Mirror operation completed (simplified)',
|
|
416
|
+
data: {
|
|
417
|
+
source,
|
|
418
|
+
target,
|
|
419
|
+
bidirectional,
|
|
420
|
+
note: 'Full mirror implementation would sync between different providers'
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
async hasUncommittedChanges(projectPath) {
|
|
425
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
426
|
+
try {
|
|
427
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
428
|
+
return statusResult.output.trim().length > 0;
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
return false;
|
|
332
432
|
}
|
|
333
433
|
}
|
|
334
434
|
};
|