@andrebuzeli/git-mcp 2.39.0 → 2.41.0
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 +6 -9
- package/dist/server.js.map +1 -1
- package/dist/tools/git-update-project.d.ts +222 -2
- package/dist/tools/git-update-project.d.ts.map +1 -1
- package/dist/tools/git-update-project.js +673 -23
- package/dist/tools/git-update-project.js.map +1 -1
- package/dist/utils/git-operations.d.ts +4 -0
- package/dist/utils/git-operations.d.ts.map +1 -1
- package/dist/utils/git-operations.js +6 -0
- package/dist/utils/git-operations.js.map +1 -1
- package/package.json +3 -3
|
@@ -5,15 +5,82 @@ 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
7
|
const git_operations_js_1 = require("../utils/git-operations.js");
|
|
8
|
-
const GitUpdateProjectInputSchema = zod_1.z.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
const GitUpdateProjectInputSchema = zod_1.z.discriminatedUnion('action', [
|
|
9
|
+
// Action: update - commit incremental
|
|
10
|
+
zod_1.z.object({
|
|
11
|
+
action: zod_1.z.literal('update'),
|
|
12
|
+
repo: zod_1.z.string(),
|
|
13
|
+
projectPath: zod_1.z.string(),
|
|
14
|
+
provider: zod_1.z.enum(['gitea', 'github']),
|
|
15
|
+
message: zod_1.z.string().min(1, 'Commit message is required'),
|
|
16
|
+
branch: zod_1.z.string().optional(),
|
|
17
|
+
forcePush: zod_1.z.boolean().optional()
|
|
18
|
+
}),
|
|
19
|
+
// Action: status - verificar status do repositório
|
|
20
|
+
zod_1.z.object({
|
|
21
|
+
action: zod_1.z.literal('status'),
|
|
22
|
+
projectPath: zod_1.z.string(),
|
|
23
|
+
detailed: zod_1.z.boolean().optional()
|
|
24
|
+
}),
|
|
25
|
+
// Action: diff - mostrar diferenças
|
|
26
|
+
zod_1.z.object({
|
|
27
|
+
action: zod_1.z.literal('diff'),
|
|
28
|
+
projectPath: zod_1.z.string(),
|
|
29
|
+
staged: zod_1.z.boolean().optional(),
|
|
30
|
+
nameOnly: zod_1.z.boolean().optional(),
|
|
31
|
+
commit: zod_1.z.string().optional()
|
|
32
|
+
}),
|
|
33
|
+
// Action: log - histórico de commits
|
|
34
|
+
zod_1.z.object({
|
|
35
|
+
action: zod_1.z.literal('log'),
|
|
36
|
+
projectPath: zod_1.z.string(),
|
|
37
|
+
maxCount: zod_1.z.number().optional(),
|
|
38
|
+
oneline: zod_1.z.boolean().optional(),
|
|
39
|
+
branch: zod_1.z.string().optional()
|
|
40
|
+
}),
|
|
41
|
+
// Action: reset - desfazer mudanças
|
|
42
|
+
zod_1.z.object({
|
|
43
|
+
action: zod_1.z.literal('reset'),
|
|
44
|
+
projectPath: zod_1.z.string(),
|
|
45
|
+
mode: zod_1.z.enum(['soft', 'mixed', 'hard']).optional(),
|
|
46
|
+
commit: zod_1.z.string().optional()
|
|
47
|
+
}),
|
|
48
|
+
// Action: stash - gerenciar mudanças temporárias
|
|
49
|
+
zod_1.z.object({
|
|
50
|
+
action: zod_1.z.literal('stash'),
|
|
51
|
+
projectPath: zod_1.z.string(),
|
|
52
|
+
operation: zod_1.z.enum(['save', 'pop', 'apply', 'list', 'drop', 'clear']).optional(),
|
|
53
|
+
message: zod_1.z.string().optional()
|
|
54
|
+
}),
|
|
55
|
+
// Action: pull - atualizar do remoto
|
|
56
|
+
zod_1.z.object({
|
|
57
|
+
action: zod_1.z.literal('pull'),
|
|
58
|
+
projectPath: zod_1.z.string(),
|
|
59
|
+
branch: zod_1.z.string().optional()
|
|
60
|
+
}),
|
|
61
|
+
// Action: sync - sincronização completa (pull + commit + push)
|
|
62
|
+
zod_1.z.object({
|
|
63
|
+
action: zod_1.z.literal('sync'),
|
|
64
|
+
repo: zod_1.z.string(),
|
|
65
|
+
projectPath: zod_1.z.string(),
|
|
66
|
+
provider: zod_1.z.enum(['gitea', 'github']),
|
|
67
|
+
message: zod_1.z.string().min(1, 'Commit message is required'),
|
|
68
|
+
branch: zod_1.z.string().optional(),
|
|
69
|
+
forcePush: zod_1.z.boolean().optional()
|
|
70
|
+
}),
|
|
71
|
+
// Action: init - upload inicial completo (substitui upload-project)
|
|
72
|
+
zod_1.z.object({
|
|
73
|
+
action: zod_1.z.literal('init'),
|
|
74
|
+
repo: zod_1.z.string(),
|
|
75
|
+
projectPath: zod_1.z.string(),
|
|
76
|
+
provider: zod_1.z.enum(['gitea', 'github']),
|
|
77
|
+
message: zod_1.z.string().min(1, 'Commit message is required'),
|
|
78
|
+
branch: zod_1.z.string().optional(),
|
|
79
|
+
createRepo: zod_1.z.boolean().optional(),
|
|
80
|
+
forcePush: zod_1.z.boolean().optional(),
|
|
81
|
+
granular: zod_1.z.boolean().optional() // commits granulares por tipo de arquivo
|
|
82
|
+
})
|
|
83
|
+
]);
|
|
17
84
|
const GitUpdateProjectResultSchema = zod_1.z.object({
|
|
18
85
|
success: zod_1.z.boolean(),
|
|
19
86
|
action: zod_1.z.string(),
|
|
@@ -23,33 +90,68 @@ const GitUpdateProjectResultSchema = zod_1.z.object({
|
|
|
23
90
|
});
|
|
24
91
|
exports.gitUpdateProjectTool = {
|
|
25
92
|
name: 'git-update-project',
|
|
26
|
-
description: 'tool:
|
|
93
|
+
description: 'tool: Gerenciamento COMPLETO de projetos Git (local + remoto)\\n──────────────\\n9 ACTIONS DISPONÍVEIS:\\n• init: UPLOAD INICIAL completo (substitui upload-project)\\n• update: Commit incremental automático\\n• status: Verificar status do repositório\\n• diff: Mostrar diferenças entre versões\\n• log: Histórico de commits\\n• reset: Desfazer mudanças\\n• stash: Gerenciar mudanças temporárias\\n• pull: Atualizar do repositório remoto\\n• sync: Sincronização completa (pull+commit+push)\\n───────────────\\nCOMMITS REAIS + RASTREABILIDADE TOTAL\\nFunciona com GitHub, Gitea e qualquer repositório Git',
|
|
27
94
|
inputSchema: {
|
|
28
95
|
type: 'object',
|
|
29
96
|
properties: {
|
|
30
|
-
action: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
97
|
+
action: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
enum: ['init', 'update', 'status', 'diff', 'log', 'reset', 'stash', 'pull', 'sync'],
|
|
100
|
+
description: 'Action to perform'
|
|
101
|
+
},
|
|
102
|
+
// Parâmetros comuns
|
|
103
|
+
repo: { type: 'string', description: 'Repository name (required for init/update/sync)' },
|
|
104
|
+
projectPath: { type: 'string', description: 'Local project path (required for all)' },
|
|
105
|
+
provider: { type: 'string', enum: ['gitea', 'github'], description: 'Provider (required for init/update/sync)' },
|
|
106
|
+
// Parâmetros específicos
|
|
107
|
+
message: { type: 'string', description: 'Commit message (required for init/update/sync)' },
|
|
108
|
+
branch: { type: 'string', description: 'Branch name', default: 'main' },
|
|
109
|
+
forcePush: { type: 'boolean', description: 'Force push', default: false },
|
|
110
|
+
createRepo: { type: 'boolean', description: 'Create remote repository if not exists', default: false },
|
|
111
|
+
granular: { type: 'boolean', description: 'Create granular commits by file type for init', default: false },
|
|
112
|
+
detailed: { type: 'boolean', description: 'Detailed output for status', default: false },
|
|
113
|
+
staged: { type: 'boolean', description: 'Show staged changes for diff', default: false },
|
|
114
|
+
nameOnly: { type: 'boolean', description: 'Show only filenames for diff', default: false },
|
|
115
|
+
commit: { type: 'string', description: 'Target commit for reset/diff' },
|
|
116
|
+
maxCount: { type: 'number', description: 'Max commits for log', default: 10 },
|
|
117
|
+
oneline: { type: 'boolean', description: 'One line format for log', default: true },
|
|
118
|
+
mode: { type: 'string', enum: ['soft', 'mixed', 'hard'], description: 'Reset mode', default: 'mixed' },
|
|
119
|
+
operation: { type: 'string', enum: ['save', 'pop', 'apply', 'list', 'drop', 'clear'], description: 'Stash operation', default: 'save' }
|
|
37
120
|
},
|
|
38
|
-
required: ['action', '
|
|
121
|
+
required: ['action', 'projectPath']
|
|
39
122
|
},
|
|
40
123
|
async handler(input) {
|
|
41
124
|
try {
|
|
42
125
|
const validatedInput = GitUpdateProjectInputSchema.parse(input);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
126
|
+
// Roteamento baseado na action
|
|
127
|
+
switch (validatedInput.action) {
|
|
128
|
+
case 'init':
|
|
129
|
+
return await this.handleInit(validatedInput);
|
|
130
|
+
case 'update':
|
|
131
|
+
return await this.handleUpdate(validatedInput);
|
|
132
|
+
case 'status':
|
|
133
|
+
return await this.handleStatus(validatedInput);
|
|
134
|
+
case 'diff':
|
|
135
|
+
return await this.handleDiff(validatedInput);
|
|
136
|
+
case 'log':
|
|
137
|
+
return await this.handleLog(validatedInput);
|
|
138
|
+
case 'reset':
|
|
139
|
+
return await this.handleReset(validatedInput);
|
|
140
|
+
case 'stash':
|
|
141
|
+
return await this.handleStash(validatedInput);
|
|
142
|
+
case 'pull':
|
|
143
|
+
return await this.handlePull(validatedInput);
|
|
144
|
+
case 'sync':
|
|
145
|
+
return await this.handleSync(validatedInput);
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`Action '${validatedInput.action}' não suportada`);
|
|
148
|
+
}
|
|
47
149
|
}
|
|
48
150
|
catch (error) {
|
|
49
151
|
return {
|
|
50
152
|
success: false,
|
|
51
153
|
action: input.action,
|
|
52
|
-
message:
|
|
154
|
+
message: `Erro na operação ${input.action}`,
|
|
53
155
|
error: error instanceof Error ? error.message : String(error)
|
|
54
156
|
};
|
|
55
157
|
}
|
|
@@ -183,6 +285,554 @@ exports.gitUpdateProjectTool = {
|
|
|
183
285
|
error: error instanceof Error ? error.message : String(error)
|
|
184
286
|
};
|
|
185
287
|
}
|
|
288
|
+
},
|
|
289
|
+
// Action: status - verificar status do repositório
|
|
290
|
+
async handleStatus(params) {
|
|
291
|
+
try {
|
|
292
|
+
const { projectPath, detailed = false } = params;
|
|
293
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
294
|
+
// Verificar se é um repositório Git
|
|
295
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
296
|
+
if (!isGitRepo) {
|
|
297
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
298
|
+
}
|
|
299
|
+
// Obter status detalhado
|
|
300
|
+
const statusResult = await gitOps.status({ porcelain: false });
|
|
301
|
+
if (!statusResult.success) {
|
|
302
|
+
throw new Error(`Falha ao obter status: ${statusResult.error}`);
|
|
303
|
+
}
|
|
304
|
+
// Obter branch atual
|
|
305
|
+
const branchResult = await gitOps.getCurrentBranch();
|
|
306
|
+
const currentBranch = branchResult.success ? branchResult.output.trim() : 'unknown';
|
|
307
|
+
// Contar mudanças
|
|
308
|
+
const porcelainResult = await gitOps.status({ porcelain: true });
|
|
309
|
+
const changes = porcelainResult.success ? porcelainResult.output.trim() : '';
|
|
310
|
+
const filesChanged = changes ? changes.split('\n').filter(line => line.trim().length > 0).length : 0;
|
|
311
|
+
return {
|
|
312
|
+
success: true,
|
|
313
|
+
action: 'status',
|
|
314
|
+
message: `Status do repositório obtido com sucesso`,
|
|
315
|
+
data: {
|
|
316
|
+
currentBranch,
|
|
317
|
+
filesChanged,
|
|
318
|
+
hasChanges: filesChanged > 0,
|
|
319
|
+
statusOutput: detailed ? statusResult.output : statusResult.output.split('\n').slice(0, 10).join('\n'),
|
|
320
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0',
|
|
321
|
+
detailed
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
return {
|
|
327
|
+
success: false,
|
|
328
|
+
action: 'status',
|
|
329
|
+
message: 'Erro ao verificar status do repositório',
|
|
330
|
+
error: error instanceof Error ? error.message : String(error)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
// Action: diff - mostrar diferenças
|
|
335
|
+
async handleDiff(params) {
|
|
336
|
+
try {
|
|
337
|
+
const { projectPath, staged = false, nameOnly = false, commit } = params;
|
|
338
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
339
|
+
// Verificar se é um repositório Git
|
|
340
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
341
|
+
if (!isGitRepo) {
|
|
342
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
343
|
+
}
|
|
344
|
+
// Obter diferenças
|
|
345
|
+
const diffOptions = {};
|
|
346
|
+
if (staged)
|
|
347
|
+
diffOptions.cached = true;
|
|
348
|
+
if (nameOnly)
|
|
349
|
+
diffOptions.nameOnly = true;
|
|
350
|
+
if (commit)
|
|
351
|
+
diffOptions.commit = commit;
|
|
352
|
+
const diffResult = await gitOps.diff(diffOptions);
|
|
353
|
+
if (!diffResult.success && diffResult.error) {
|
|
354
|
+
throw new Error(`Falha ao obter diferenças: ${diffResult.error}`);
|
|
355
|
+
}
|
|
356
|
+
const hasDifferences = diffResult.output.trim().length > 0;
|
|
357
|
+
return {
|
|
358
|
+
success: true,
|
|
359
|
+
action: 'diff',
|
|
360
|
+
message: hasDifferences ? 'Diferenças encontradas' : 'Nenhuma diferença encontrada',
|
|
361
|
+
data: {
|
|
362
|
+
hasDifferences,
|
|
363
|
+
diffOutput: diffResult.output,
|
|
364
|
+
staged,
|
|
365
|
+
nameOnly,
|
|
366
|
+
commit: commit || 'HEAD',
|
|
367
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
return {
|
|
373
|
+
success: false,
|
|
374
|
+
action: 'diff',
|
|
375
|
+
message: 'Erro ao obter diferenças',
|
|
376
|
+
error: error instanceof Error ? error.message : String(error)
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
// Action: log - histórico de commits
|
|
381
|
+
async handleLog(params) {
|
|
382
|
+
try {
|
|
383
|
+
const { projectPath, maxCount = 10, oneline = true, branch } = params;
|
|
384
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
385
|
+
// Verificar se é um repositório Git
|
|
386
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
387
|
+
if (!isGitRepo) {
|
|
388
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
389
|
+
}
|
|
390
|
+
// Obter histórico de commits
|
|
391
|
+
const logOptions = {};
|
|
392
|
+
if (maxCount)
|
|
393
|
+
logOptions.maxCount = maxCount;
|
|
394
|
+
if (oneline)
|
|
395
|
+
logOptions.oneline = true;
|
|
396
|
+
if (branch)
|
|
397
|
+
logOptions.branches = [branch];
|
|
398
|
+
const logResult = await gitOps.log(logOptions);
|
|
399
|
+
if (!logResult.success) {
|
|
400
|
+
throw new Error(`Falha ao obter histórico: ${logResult.error}`);
|
|
401
|
+
}
|
|
402
|
+
const commits = logResult.output.trim();
|
|
403
|
+
const commitCount = commits ? commits.split('\n').filter(line => line.trim().length > 0).length : 0;
|
|
404
|
+
return {
|
|
405
|
+
success: true,
|
|
406
|
+
action: 'log',
|
|
407
|
+
message: `${commitCount} commits encontrados no histórico`,
|
|
408
|
+
data: {
|
|
409
|
+
commits: commits || 'Nenhum commit encontrado',
|
|
410
|
+
commitCount,
|
|
411
|
+
maxCount,
|
|
412
|
+
oneline,
|
|
413
|
+
branch: branch || 'all',
|
|
414
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
return {
|
|
420
|
+
success: false,
|
|
421
|
+
action: 'log',
|
|
422
|
+
message: 'Erro ao obter histórico de commits',
|
|
423
|
+
error: error instanceof Error ? error.message : String(error)
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
// Action: reset - desfazer mudanças
|
|
428
|
+
async handleReset(params) {
|
|
429
|
+
try {
|
|
430
|
+
const { projectPath, mode = 'mixed', commit } = params;
|
|
431
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
432
|
+
// Verificar se é um repositório Git
|
|
433
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
434
|
+
if (!isGitRepo) {
|
|
435
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
436
|
+
}
|
|
437
|
+
// Executar reset
|
|
438
|
+
const resetOptions = {};
|
|
439
|
+
if (commit) {
|
|
440
|
+
resetOptions.target = commit;
|
|
441
|
+
}
|
|
442
|
+
const resetResult = await gitOps.reset(mode, resetOptions);
|
|
443
|
+
if (!resetResult.success) {
|
|
444
|
+
throw new Error(`Falha no reset: ${resetResult.error}`);
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
success: true,
|
|
448
|
+
action: 'reset',
|
|
449
|
+
message: `Reset ${mode} executado com sucesso`,
|
|
450
|
+
data: {
|
|
451
|
+
mode,
|
|
452
|
+
targetCommit: commit || 'HEAD',
|
|
453
|
+
resetOutput: resetResult.output,
|
|
454
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
action: 'reset',
|
|
462
|
+
message: 'Erro ao executar reset',
|
|
463
|
+
error: error instanceof Error ? error.message : String(error)
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
// Action: stash - gerenciar mudanças temporárias
|
|
468
|
+
async handleStash(params) {
|
|
469
|
+
try {
|
|
470
|
+
const { projectPath, operation = 'save', message } = params;
|
|
471
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
472
|
+
// Verificar se é um repositório Git
|
|
473
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
474
|
+
if (!isGitRepo) {
|
|
475
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
476
|
+
}
|
|
477
|
+
let stashResult;
|
|
478
|
+
switch (operation) {
|
|
479
|
+
case 'save':
|
|
480
|
+
stashResult = await gitOps.stash('push', { message });
|
|
481
|
+
break;
|
|
482
|
+
case 'pop':
|
|
483
|
+
stashResult = await gitOps.stash('pop');
|
|
484
|
+
break;
|
|
485
|
+
case 'apply':
|
|
486
|
+
stashResult = await gitOps.stash('apply');
|
|
487
|
+
break;
|
|
488
|
+
case 'list':
|
|
489
|
+
stashResult = await gitOps.stash('list');
|
|
490
|
+
break;
|
|
491
|
+
case 'drop':
|
|
492
|
+
stashResult = await gitOps.stash('drop');
|
|
493
|
+
break;
|
|
494
|
+
case 'clear':
|
|
495
|
+
stashResult = await gitOps.stash('clear');
|
|
496
|
+
break;
|
|
497
|
+
default:
|
|
498
|
+
throw new Error(`Operação de stash '${operation}' não suportada`);
|
|
499
|
+
}
|
|
500
|
+
if (!stashResult.success) {
|
|
501
|
+
throw new Error(`Falha na operação stash ${operation}: ${stashResult.error}`);
|
|
502
|
+
}
|
|
503
|
+
return {
|
|
504
|
+
success: true,
|
|
505
|
+
action: 'stash',
|
|
506
|
+
message: `Operação stash '${operation}' executada com sucesso`,
|
|
507
|
+
data: {
|
|
508
|
+
operation,
|
|
509
|
+
message: message || undefined,
|
|
510
|
+
stashOutput: stashResult.output,
|
|
511
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
return {
|
|
517
|
+
success: false,
|
|
518
|
+
action: 'stash',
|
|
519
|
+
message: 'Erro na operação de stash',
|
|
520
|
+
error: error instanceof Error ? error.message : String(error)
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
// Action: pull - atualizar do remoto
|
|
525
|
+
async handlePull(params) {
|
|
526
|
+
try {
|
|
527
|
+
const { projectPath, branch } = params;
|
|
528
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
529
|
+
// Verificar se é um repositório Git
|
|
530
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
531
|
+
if (!isGitRepo) {
|
|
532
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
533
|
+
}
|
|
534
|
+
// Executar pull
|
|
535
|
+
const pullResult = await gitOps.pull('origin', branch);
|
|
536
|
+
if (!pullResult.success) {
|
|
537
|
+
throw new Error(`Falha no pull: ${pullResult.error}`);
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
success: true,
|
|
541
|
+
action: 'pull',
|
|
542
|
+
message: `Pull do remoto executado com sucesso`,
|
|
543
|
+
data: {
|
|
544
|
+
remote: 'origin',
|
|
545
|
+
branch: branch || 'current',
|
|
546
|
+
pullOutput: pullResult.output,
|
|
547
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
return {
|
|
553
|
+
success: false,
|
|
554
|
+
action: 'pull',
|
|
555
|
+
message: 'Erro ao executar pull',
|
|
556
|
+
error: error instanceof Error ? error.message : String(error)
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
// Action: sync - sincronização completa (pull + commit + push)
|
|
561
|
+
async handleSync(params) {
|
|
562
|
+
try {
|
|
563
|
+
const { repo, projectPath, message, branch = 'main', forcePush = false, provider: providerName } = params;
|
|
564
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
565
|
+
// Verificar se é um repositório Git
|
|
566
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
567
|
+
if (!isGitRepo) {
|
|
568
|
+
throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
|
|
569
|
+
}
|
|
570
|
+
const syncResults = {
|
|
571
|
+
pull: { success: false, output: '', error: '' },
|
|
572
|
+
commit: { success: false, output: '', error: '', created: false },
|
|
573
|
+
push: { success: false, output: '', error: '' }
|
|
574
|
+
};
|
|
575
|
+
// 1. Pull do remoto
|
|
576
|
+
try {
|
|
577
|
+
const pullResult = await gitOps.pull('origin', branch);
|
|
578
|
+
syncResults.pull = {
|
|
579
|
+
success: pullResult.success,
|
|
580
|
+
output: pullResult.output,
|
|
581
|
+
error: pullResult.error || ''
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
catch (pullErr) {
|
|
585
|
+
syncResults.pull.error = pullErr instanceof Error ? pullErr.message : String(pullErr);
|
|
586
|
+
}
|
|
587
|
+
// 2. Verificar se há mudanças após pull
|
|
588
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
589
|
+
const hasChanges = statusResult.success && statusResult.output.trim().length > 0;
|
|
590
|
+
if (hasChanges) {
|
|
591
|
+
// Fazer commit das mudanças
|
|
592
|
+
const addResult = await gitOps.addFiles(['.']);
|
|
593
|
+
if (addResult.success) {
|
|
594
|
+
const commitResult = await gitOps.commit(message);
|
|
595
|
+
syncResults.commit = {
|
|
596
|
+
success: commitResult.success,
|
|
597
|
+
output: commitResult.output,
|
|
598
|
+
error: commitResult.error || '',
|
|
599
|
+
created: commitResult.success
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
syncResults.commit.error = `Falha ao adicionar arquivos: ${addResult.error}`;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// 3. Push se commit foi criado
|
|
607
|
+
if (syncResults.commit.created) {
|
|
608
|
+
try {
|
|
609
|
+
const pushOptions = {};
|
|
610
|
+
if (forcePush)
|
|
611
|
+
pushOptions.force = true;
|
|
612
|
+
const pushResult = await gitOps.push('origin', branch, pushOptions);
|
|
613
|
+
syncResults.push = {
|
|
614
|
+
success: pushResult.success,
|
|
615
|
+
output: pushResult.output,
|
|
616
|
+
error: pushResult.error || ''
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
catch (pushErr) {
|
|
620
|
+
syncResults.push.error = pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
const overallSuccess = syncResults.pull.success && (!hasChanges || syncResults.commit.success);
|
|
624
|
+
return {
|
|
625
|
+
success: overallSuccess,
|
|
626
|
+
action: 'sync',
|
|
627
|
+
message: `Sincronização ${overallSuccess ? 'concluída' : 'com problemas'}`,
|
|
628
|
+
data: {
|
|
629
|
+
repo,
|
|
630
|
+
branch,
|
|
631
|
+
provider: providerName,
|
|
632
|
+
syncResults,
|
|
633
|
+
hasChanges,
|
|
634
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.38.0',
|
|
635
|
+
summary: {
|
|
636
|
+
pullSuccessful: syncResults.pull.success,
|
|
637
|
+
commitCreated: syncResults.commit.created,
|
|
638
|
+
pushSuccessful: syncResults.push.success
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
catch (error) {
|
|
644
|
+
return {
|
|
645
|
+
success: false,
|
|
646
|
+
action: 'sync',
|
|
647
|
+
message: 'Erro na sincronização completa',
|
|
648
|
+
error: error instanceof Error ? error.message : String(error)
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
// Action: init - upload inicial completo (substitui upload-project)
|
|
653
|
+
async handleInit(params) {
|
|
654
|
+
try {
|
|
655
|
+
const { repo, projectPath, message, branch = 'main', forcePush = false, createRepo = false, granular = false, provider: providerName } = params;
|
|
656
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
657
|
+
// Aplicar auto-detecção de usuário
|
|
658
|
+
const processedInput = await (0, user_detection_js_1.applyAutoUserDetection)(params, providerName);
|
|
659
|
+
const provider = index_js_1.globalProviderFactory.getProvider(processedInput.provider);
|
|
660
|
+
if (!provider) {
|
|
661
|
+
throw new Error(`Provider '${providerName}' não encontrado`);
|
|
662
|
+
}
|
|
663
|
+
const currentUser = await provider.getCurrentUser();
|
|
664
|
+
const owner = currentUser.login;
|
|
665
|
+
// 1. Inicializar repositório Git local se necessário
|
|
666
|
+
const isGitRepo = await gitOps.isGitRepository();
|
|
667
|
+
if (!isGitRepo) {
|
|
668
|
+
const initResult = await gitOps.initRepository();
|
|
669
|
+
if (!initResult.success) {
|
|
670
|
+
throw new Error(`Falha ao inicializar repositório Git: ${initResult.error}`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
// 2. Verificar/criar repositório remoto
|
|
674
|
+
let repoExists = true;
|
|
675
|
+
try {
|
|
676
|
+
await provider.getRepository(owner, repo);
|
|
677
|
+
}
|
|
678
|
+
catch (error) {
|
|
679
|
+
repoExists = false;
|
|
680
|
+
if (createRepo) {
|
|
681
|
+
try {
|
|
682
|
+
await provider.createRepository(repo, `Projeto ${repo}`, false);
|
|
683
|
+
repoExists = true;
|
|
684
|
+
}
|
|
685
|
+
catch (createError) {
|
|
686
|
+
console.warn(`Aviso: Não foi possível criar repositório: ${createError instanceof Error ? createError.message : String(createError)}`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
throw new Error(`Repositório '${repo}' não existe. Use createRepo: true para criar automaticamente.`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// 3. Preparar arquivos para commit
|
|
694
|
+
let filesCommitted = 0;
|
|
695
|
+
let commitsCreated = 0;
|
|
696
|
+
if (granular) {
|
|
697
|
+
// Commits granulares por tipo de arquivo (melhor rastreabilidade)
|
|
698
|
+
const fileGroups = await this.groupFilesByType(projectPath);
|
|
699
|
+
for (const [fileType, files] of Object.entries(fileGroups)) {
|
|
700
|
+
if (files.length > 0) {
|
|
701
|
+
// Adicionar arquivos do tipo específico
|
|
702
|
+
const addResult = await gitOps.addFiles(files);
|
|
703
|
+
if (addResult.success) {
|
|
704
|
+
// Verificar se há mudanças para commitar
|
|
705
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
706
|
+
if (statusResult.success && statusResult.output.trim()) {
|
|
707
|
+
const commitResult = await gitOps.commit(`${message} - ${fileType} files`);
|
|
708
|
+
if (commitResult.success) {
|
|
709
|
+
commitsCreated++;
|
|
710
|
+
filesCommitted += files.length;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
// Commit único de todos os arquivos (como upload-project)
|
|
719
|
+
const addResult = await gitOps.addFiles(['.']);
|
|
720
|
+
if (addResult.success) {
|
|
721
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
722
|
+
if (statusResult.success && statusResult.output.trim()) {
|
|
723
|
+
const commitResult = await gitOps.commit(message);
|
|
724
|
+
if (commitResult.success) {
|
|
725
|
+
commitsCreated++;
|
|
726
|
+
filesCommitted = statusResult.output.trim().split('\n').length;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
// 4. Configurar e fazer push para remote
|
|
732
|
+
let pushSuccessful = false;
|
|
733
|
+
const remoteUrl = provider.getRepositoryUrl(owner, repo);
|
|
734
|
+
// Configurar remote origin
|
|
735
|
+
const remoteResult = await gitOps.remote('show', 'origin');
|
|
736
|
+
if (!remoteResult.success) {
|
|
737
|
+
const addRemoteResult = await gitOps.remote('add', 'origin', remoteUrl);
|
|
738
|
+
if (!addRemoteResult.success && !addRemoteResult.error?.includes('already exists')) {
|
|
739
|
+
console.warn(`Aviso: Falha ao adicionar remote: ${addRemoteResult.error}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
// Atualizar URL se necessário
|
|
744
|
+
const setUrlResult = await gitOps.remote('set-url', 'origin', remoteUrl);
|
|
745
|
+
if (!setUrlResult.success) {
|
|
746
|
+
console.warn(`Aviso: Falha ao atualizar remote URL: ${setUrlResult.error}`);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
// Fazer push
|
|
750
|
+
if (commitsCreated > 0) {
|
|
751
|
+
try {
|
|
752
|
+
const pushOptions = { setUpstream: true };
|
|
753
|
+
if (forcePush)
|
|
754
|
+
pushOptions.force = true;
|
|
755
|
+
const pushResult = await gitOps.push('origin', branch, pushOptions);
|
|
756
|
+
pushSuccessful = pushResult.success;
|
|
757
|
+
if (!pushSuccessful) {
|
|
758
|
+
console.warn(`Aviso: Push falhou: ${pushResult.error}`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
catch (pushErr) {
|
|
762
|
+
console.warn(`Aviso: Erro no push: ${pushErr instanceof Error ? pushErr.message : String(pushErr)}`);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
success: true,
|
|
767
|
+
action: 'init',
|
|
768
|
+
message: `Upload inicial concluído com ${commitsCreated} commit(s) e ${filesCommitted} arquivo(s)`,
|
|
769
|
+
data: {
|
|
770
|
+
repo,
|
|
771
|
+
branch,
|
|
772
|
+
provider: providerName,
|
|
773
|
+
owner,
|
|
774
|
+
remoteUrl,
|
|
775
|
+
repositoryCreated: !repoExists && createRepo,
|
|
776
|
+
gitInit: !isGitRepo,
|
|
777
|
+
commitsCreated,
|
|
778
|
+
filesCommitted,
|
|
779
|
+
pushSuccessful,
|
|
780
|
+
granular,
|
|
781
|
+
implementation: 'REAL_GIT_OPERATIONS_v2.40.0',
|
|
782
|
+
note: granular ?
|
|
783
|
+
'Commits granulares criados por tipo de arquivo para melhor rastreabilidade' :
|
|
784
|
+
'Commit único criado (como upload-project original)'
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
catch (error) {
|
|
789
|
+
return {
|
|
790
|
+
success: false,
|
|
791
|
+
action: 'init',
|
|
792
|
+
message: 'Erro no upload inicial do projeto',
|
|
793
|
+
error: error instanceof Error ? error.message : String(error)
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
// Helper method para agrupar arquivos por tipo
|
|
798
|
+
async groupFilesByType(projectPath) {
|
|
799
|
+
const gitOps = new git_operations_js_1.GitOperations(projectPath);
|
|
800
|
+
const statusResult = await gitOps.status({ porcelain: true });
|
|
801
|
+
if (!statusResult.success) {
|
|
802
|
+
return {};
|
|
803
|
+
}
|
|
804
|
+
const lines = statusResult.output.trim().split('\n').filter(line => line.trim());
|
|
805
|
+
const fileGroups = {
|
|
806
|
+
'config': [],
|
|
807
|
+
'source': [],
|
|
808
|
+
'documentation': [],
|
|
809
|
+
'assets': [],
|
|
810
|
+
'other': []
|
|
811
|
+
};
|
|
812
|
+
for (const line of lines) {
|
|
813
|
+
// Extrair nome do arquivo (remover status indicators)
|
|
814
|
+
const fileName = line.substring(3).trim();
|
|
815
|
+
if (fileName.includes('package.json') || fileName.includes('.config.') ||
|
|
816
|
+
fileName.includes('tsconfig') || fileName.includes('.env')) {
|
|
817
|
+
fileGroups.config.push(fileName);
|
|
818
|
+
}
|
|
819
|
+
else if (fileName.endsWith('.ts') || fileName.endsWith('.js') ||
|
|
820
|
+
fileName.endsWith('.py') || fileName.endsWith('.java')) {
|
|
821
|
+
fileGroups.source.push(fileName);
|
|
822
|
+
}
|
|
823
|
+
else if (fileName.endsWith('.md') || fileName.endsWith('.txt') ||
|
|
824
|
+
fileName.includes('README') || fileName.includes('LICENSE')) {
|
|
825
|
+
fileGroups.documentation.push(fileName);
|
|
826
|
+
}
|
|
827
|
+
else if (fileName.endsWith('.png') || fileName.endsWith('.jpg') ||
|
|
828
|
+
fileName.endsWith('.svg') || fileName.endsWith('.css')) {
|
|
829
|
+
fileGroups.assets.push(fileName);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
fileGroups.other.push(fileName);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
return fileGroups;
|
|
186
836
|
}
|
|
187
837
|
};
|
|
188
838
|
//# sourceMappingURL=git-update-project.js.map
|