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