@asaro/git-cli 1.0.0 → 1.0.2
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/README.md +20 -6
- package/dist/adapters/git/SimpleGitAdapter.js +18 -0
- package/dist/adapters/github/GitHubAPIAdapter.js +9 -1
- package/dist/adapters/storage/FileSecureStorageAdapter.js +31 -10
- package/dist/adapters/ui/ConsoleUIAdapter.js +15 -3
- package/dist/{factories → commands}/CommandFactory.js +13 -10
- package/dist/commands/CommitCommand.js +80 -17
- package/dist/commands/DiffCommand.js +60 -0
- package/dist/commands/LogCommand.js +90 -20
- package/dist/commands/PushCommand.js +1 -3
- package/dist/commands/RepositoryCommand.js +349 -105
- package/dist/commands/github/GitHubReposCommand.js +1 -3
- package/dist/core/services/GitHubService.test.js +61 -0
- package/dist/index.js +19 -2
- package/package.json +5 -2
- package/dist/commit.js +0 -98
- package/dist/core/errors/GitError.js +0 -19
- package/dist/core/services/GitService.js +0 -44
- package/dist/estado.js +0 -97
- package/dist/integracion.js +0 -152
- package/dist/ramas.js +0 -178
- package/dist/remoto.js +0 -129
- package/dist/repositorio.js +0 -159
- package/dist/stash.js +0 -169
- package/dist/utils.js +0 -49
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asaro/git-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Git CLI interactivo en español para gestionar repositorios Git y GitHub",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"build": "tsc",
|
|
15
15
|
"start": "node --experimental-vm-modules dist/index.js",
|
|
16
16
|
"dev": "node --loader ts-node/esm src/index.ts",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"prepare": "npm run build",
|
|
17
19
|
"prepublishOnly": "npm run build"
|
|
18
20
|
},
|
|
19
21
|
"keywords": [
|
|
@@ -45,7 +47,8 @@
|
|
|
45
47
|
"@types/crypto-js": "^4.2.2",
|
|
46
48
|
"@types/node": "^25.9.2",
|
|
47
49
|
"ts-node": "^10.9.2",
|
|
48
|
-
"typescript": "^6.0.3"
|
|
50
|
+
"typescript": "^6.0.3",
|
|
51
|
+
"vitest": "^4.1.10"
|
|
49
52
|
},
|
|
50
53
|
"engines": {
|
|
51
54
|
"node": ">=18.0.0"
|
package/dist/commit.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
-
import { checkbox, input, select, confirm } from "@inquirer/prompts";
|
|
3
|
-
// ── Commit guiado (add + commit) ─────────────────────────────────────────────
|
|
4
|
-
export async function hacerCommit() {
|
|
5
|
-
if (!(await verificarRepo()))
|
|
6
|
-
return;
|
|
7
|
-
const spin = spinner("Leyendo estado del repositorio...");
|
|
8
|
-
const status = await git.status();
|
|
9
|
-
spin.stop();
|
|
10
|
-
// Armar lista de archivos disponibles para stagear
|
|
11
|
-
const disponibles = [
|
|
12
|
-
...status.modified.map(f => ({ name: c.aviso(` ~ ${f}`) + c.tenue(" (modificado)"), value: f })),
|
|
13
|
-
...status.not_added.map(f => ({ name: c.tenue(` ? ${f}`) + c.tenue(" (nuevo)"), value: f })),
|
|
14
|
-
...status.deleted.map(f => ({ name: c.error(` - ${f}`) + c.tenue(" (eliminado)"), value: f })),
|
|
15
|
-
];
|
|
16
|
-
// Si ya hay staged, ofrecer usarlos o agregar más
|
|
17
|
-
let archivosParaStage = [];
|
|
18
|
-
if (status.staged.length > 0) {
|
|
19
|
-
console.log();
|
|
20
|
-
console.log(c.info(" Ya tienes archivos preparados (staged):"));
|
|
21
|
-
status.staged.forEach(f => console.log(c.exito(` + ${f}`)));
|
|
22
|
-
console.log();
|
|
23
|
-
const usarExistentes = await confirm({
|
|
24
|
-
message: "¿Usar los archivos ya preparados sin agregar más?",
|
|
25
|
-
default: true,
|
|
26
|
-
});
|
|
27
|
-
if (!usarExistentes && disponibles.length > 0) {
|
|
28
|
-
archivosParaStage = await checkbox({
|
|
29
|
-
message: "Selecciona archivos adicionales para incluir:",
|
|
30
|
-
choices: disponibles,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
if (disponibles.length === 0) {
|
|
36
|
-
console.log(c.aviso("\n No hay cambios para hacer commit.\n"));
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
// Opción de agregar todo o seleccionar
|
|
40
|
-
const modo = await select({
|
|
41
|
-
message: "¿Qué archivos incluir en el commit?",
|
|
42
|
-
choices: [
|
|
43
|
-
{ name: "Todos los cambios (git add .)", value: "todos" },
|
|
44
|
-
{ name: "Seleccionar manualmente", value: "manual" },
|
|
45
|
-
],
|
|
46
|
-
});
|
|
47
|
-
if (modo === "todos") {
|
|
48
|
-
archivosParaStage = ["."];
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
archivosParaStage = await checkbox({
|
|
52
|
-
message: "Selecciona los archivos (espacio para marcar):",
|
|
53
|
-
choices: disponibles,
|
|
54
|
-
validate: (val) => val.length > 0 || "Debes seleccionar al menos uno.",
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
const spinAdd = spinner("Agregando archivos...");
|
|
58
|
-
await git.add(archivosParaStage);
|
|
59
|
-
spinAdd.stop();
|
|
60
|
-
console.log(c.exito(" ✓ Archivos agregados al stage"));
|
|
61
|
-
}
|
|
62
|
-
// Mensaje de commit
|
|
63
|
-
console.log();
|
|
64
|
-
separador();
|
|
65
|
-
const tipo = await select({
|
|
66
|
-
message: "Tipo de commit:",
|
|
67
|
-
choices: [
|
|
68
|
-
{ name: "feat · Nueva funcionalidad", value: "feat" },
|
|
69
|
-
{ name: "fix · Corrección de bug", value: "fix" },
|
|
70
|
-
{ name: "docs · Documentación", value: "docs" },
|
|
71
|
-
{ name: "style · Estilos / formato", value: "style" },
|
|
72
|
-
{ name: "refactor · Refactorización", value: "refactor" },
|
|
73
|
-
{ name: "test · Tests", value: "test" },
|
|
74
|
-
{ name: "chore · Tareas de mantenimiento", value: "chore" },
|
|
75
|
-
{ name: "build · Sistema de build", value: "build" },
|
|
76
|
-
{ name: "ci · CI/CD", value: "ci" },
|
|
77
|
-
{ name: "Ninguno · Mensaje libre", value: "" },
|
|
78
|
-
],
|
|
79
|
-
});
|
|
80
|
-
const descripcion = await input({
|
|
81
|
-
message: "Descripción del commit:",
|
|
82
|
-
validate: (v) => v.trim().length > 0 || "El mensaje no puede estar vacío.",
|
|
83
|
-
});
|
|
84
|
-
const mensaje = tipo ? `${tipo}: ${descripcion.trim()}` : descripcion.trim();
|
|
85
|
-
// Confirmar
|
|
86
|
-
separador();
|
|
87
|
-
console.log(c.titulo(` Commit: `) + c.info(`"${mensaje}"`));
|
|
88
|
-
separador();
|
|
89
|
-
const confirmar = await confirm({ message: "¿Confirmar commit?", default: true });
|
|
90
|
-
if (!confirmar) {
|
|
91
|
-
console.log(c.aviso(" Commit cancelado.\n"));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const spinCommit = spinner("Realizando commit...");
|
|
95
|
-
await git.commit(mensaje);
|
|
96
|
-
spinCommit.stop();
|
|
97
|
-
console.log(c.exito(`\n ✓ Commit realizado: "${mensaje}"\n`));
|
|
98
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export class GitError extends Error {
|
|
2
|
-
constructor(message) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = 'GitError';
|
|
5
|
-
Object.setPrototypeOf(this, GitError.prototype);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export class NotARepositoryError extends GitError {
|
|
9
|
-
constructor() {
|
|
10
|
-
super('Este directorio no es un repositorio Git');
|
|
11
|
-
this.name = 'NotARepositoryError';
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
export class NoChangesError extends GitError {
|
|
15
|
-
constructor() {
|
|
16
|
-
super('No hay cambios para realizar');
|
|
17
|
-
this.name = 'NoChangesError';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { NotARepositoryError } from '../errors/GitError.js';
|
|
2
|
-
export class GitService {
|
|
3
|
-
gitPort;
|
|
4
|
-
constructor(gitPort) {
|
|
5
|
-
this.gitPort = gitPort;
|
|
6
|
-
}
|
|
7
|
-
async ensureIsRepository() {
|
|
8
|
-
const isRepo = await this.gitPort.isRepository();
|
|
9
|
-
if (!isRepo) {
|
|
10
|
-
throw new NotARepositoryError();
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
async getStatus() {
|
|
14
|
-
await this.ensureIsRepository();
|
|
15
|
-
return this.gitPort.getStatus();
|
|
16
|
-
}
|
|
17
|
-
async getLog(maxCount = 10) {
|
|
18
|
-
await this.ensureIsRepository();
|
|
19
|
-
return this.gitPort.getLog(maxCount);
|
|
20
|
-
}
|
|
21
|
-
async getBranches() {
|
|
22
|
-
await this.ensureIsRepository();
|
|
23
|
-
return this.gitPort.getBranches();
|
|
24
|
-
}
|
|
25
|
-
async commitChanges(files, message) {
|
|
26
|
-
await this.ensureIsRepository();
|
|
27
|
-
if (files.length > 0) {
|
|
28
|
-
await this.gitPort.add(files);
|
|
29
|
-
}
|
|
30
|
-
await this.gitPort.commit(message);
|
|
31
|
-
}
|
|
32
|
-
async push(remote = 'origin', branch, options) {
|
|
33
|
-
await this.ensureIsRepository();
|
|
34
|
-
await this.gitPort.push(remote, branch, options);
|
|
35
|
-
}
|
|
36
|
-
async pull(remote = 'origin', branch, useRebase = false) {
|
|
37
|
-
await this.ensureIsRepository();
|
|
38
|
-
await this.gitPort.pull(remote, branch, useRebase ? { '--rebase': 'true' } : undefined);
|
|
39
|
-
}
|
|
40
|
-
async getStashList() {
|
|
41
|
-
await this.ensureIsRepository();
|
|
42
|
-
return this.gitPort.stashList();
|
|
43
|
-
}
|
|
44
|
-
}
|
package/dist/estado.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
-
import { select } from "@inquirer/prompts";
|
|
3
|
-
// ── Status detallado ─────────────────────────────────────────────────────────
|
|
4
|
-
export async function mostrarStatus() {
|
|
5
|
-
if (!(await verificarRepo()))
|
|
6
|
-
return;
|
|
7
|
-
const spin = spinner("Obteniendo estado del repositorio...");
|
|
8
|
-
const status = await git.status();
|
|
9
|
-
spin.stop();
|
|
10
|
-
const rama = status.current ?? "desconocida";
|
|
11
|
-
console.log();
|
|
12
|
-
console.log(c.titulo(" 📋 Estado del repositorio"));
|
|
13
|
-
separador();
|
|
14
|
-
console.log(` Rama actual: ${c.rama(rama)}`);
|
|
15
|
-
// Indicador de sincronía con remoto
|
|
16
|
-
if (status.ahead > 0 && status.behind > 0) {
|
|
17
|
-
console.log(c.aviso(` ⚡ ${status.ahead} commit(s) adelante y ${status.behind} atrás del remoto`));
|
|
18
|
-
}
|
|
19
|
-
else if (status.ahead > 0) {
|
|
20
|
-
console.log(c.aviso(` ↑ ${status.ahead} commit(s) por subir`));
|
|
21
|
-
}
|
|
22
|
-
else if (status.behind > 0) {
|
|
23
|
-
console.log(c.aviso(` ↓ ${status.behind} commit(s) por bajar`));
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
console.log(c.exito(" ✓ Sincronizado con el remoto"));
|
|
27
|
-
}
|
|
28
|
-
separador();
|
|
29
|
-
// Archivos staged
|
|
30
|
-
if (status.staged.length > 0) {
|
|
31
|
-
console.log(c.exito(" Preparados para commit (staged):"));
|
|
32
|
-
status.staged.forEach(f => console.log(c.exito(` + ${f}`)));
|
|
33
|
-
}
|
|
34
|
-
// Modificados sin stagear
|
|
35
|
-
if (status.modified.length > 0) {
|
|
36
|
-
console.log(c.aviso(" Modificados (sin stagear):"));
|
|
37
|
-
status.modified.forEach(f => console.log(c.aviso(` ~ ${f}`)));
|
|
38
|
-
}
|
|
39
|
-
// Sin seguimiento
|
|
40
|
-
if (status.not_added.length > 0) {
|
|
41
|
-
console.log(c.tenue(" Sin seguimiento (untracked):"));
|
|
42
|
-
status.not_added.forEach(f => console.log(c.tenue(` ? ${f}`)));
|
|
43
|
-
}
|
|
44
|
-
// Eliminados
|
|
45
|
-
if (status.deleted.length > 0) {
|
|
46
|
-
console.log(c.error(" Eliminados:"));
|
|
47
|
-
status.deleted.forEach(f => console.log(c.error(` - ${f}`)));
|
|
48
|
-
}
|
|
49
|
-
// Conflictos
|
|
50
|
-
if (status.conflicted.length > 0) {
|
|
51
|
-
console.log(c.error(" Con conflictos:"));
|
|
52
|
-
status.conflicted.forEach(f => console.log(c.error(` ✗ ${f}`)));
|
|
53
|
-
}
|
|
54
|
-
const total = status.staged.length +
|
|
55
|
-
status.modified.length +
|
|
56
|
-
status.not_added.length +
|
|
57
|
-
status.deleted.length;
|
|
58
|
-
if (total === 0) {
|
|
59
|
-
console.log(c.tenue(" Árbol de trabajo limpio."));
|
|
60
|
-
}
|
|
61
|
-
separador();
|
|
62
|
-
console.log();
|
|
63
|
-
}
|
|
64
|
-
// ── Log visual ───────────────────────────────────────────────────────────────
|
|
65
|
-
export async function mostrarLog() {
|
|
66
|
-
if (!(await verificarRepo()))
|
|
67
|
-
return;
|
|
68
|
-
const cantidad = await select({
|
|
69
|
-
message: "¿Cuántos commits mostrar?",
|
|
70
|
-
choices: [
|
|
71
|
-
{ name: "Últimos 10", value: 10 },
|
|
72
|
-
{ name: "Últimos 20", value: 20 },
|
|
73
|
-
{ name: "Últimos 50", value: 50 },
|
|
74
|
-
],
|
|
75
|
-
});
|
|
76
|
-
const spin = spinner("Cargando historial...");
|
|
77
|
-
const log = await git.log({ maxCount: cantidad });
|
|
78
|
-
spin.stop();
|
|
79
|
-
console.log();
|
|
80
|
-
console.log(c.titulo(` 📜 Últimos ${cantidad} commits`));
|
|
81
|
-
separador();
|
|
82
|
-
log.all.forEach((commit, i) => {
|
|
83
|
-
const esUltimo = i === 0;
|
|
84
|
-
const prefijo = esUltimo ? "◉" : "○";
|
|
85
|
-
const fecha = new Date(commit.date).toLocaleDateString("es-CL", {
|
|
86
|
-
day: "2-digit",
|
|
87
|
-
month: "short",
|
|
88
|
-
year: "numeric",
|
|
89
|
-
});
|
|
90
|
-
console.log(` ${c.hash(prefijo)} ${c.hash(commit.hash.slice(0, 7))}` +
|
|
91
|
-
` ${c.tenue(fecha)} ` +
|
|
92
|
-
`${c.titulo(commit.message.slice(0, 45))}` +
|
|
93
|
-
` ${c.tenue(commit.author_name)}`);
|
|
94
|
-
});
|
|
95
|
-
separador();
|
|
96
|
-
console.log();
|
|
97
|
-
}
|
package/dist/integracion.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
-
import { select, confirm } from "@inquirer/prompts";
|
|
3
|
-
// ── Menú Merge / Rebase ───────────────────────────────────────────────────────
|
|
4
|
-
export async function menuMergeRebase() {
|
|
5
|
-
if (!(await verificarRepo()))
|
|
6
|
-
return;
|
|
7
|
-
const accion = await select({
|
|
8
|
-
message: "Integración — ¿qué deseas hacer?",
|
|
9
|
-
choices: [
|
|
10
|
-
{ name: "Merge (fusionar rama en la actual)", value: "merge" },
|
|
11
|
-
{ name: "Rebase (reescribir historial sobre base)", value: "rebase" },
|
|
12
|
-
{ name: "Abortar merge en curso", value: "abortMerge" },
|
|
13
|
-
{ name: "Abortar rebase en curso", value: "abortRebase" },
|
|
14
|
-
{ name: "← Volver al menú principal", value: "volver" },
|
|
15
|
-
],
|
|
16
|
-
});
|
|
17
|
-
switch (accion) {
|
|
18
|
-
case "merge": return hacerMerge();
|
|
19
|
-
case "rebase": return hacerRebase();
|
|
20
|
-
case "abortMerge": return abortarMerge();
|
|
21
|
-
case "abortRebase": return abortarRebase();
|
|
22
|
-
case "volver": return;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
// ── Merge ─────────────────────────────────────────────────────────────────────
|
|
26
|
-
async function hacerMerge() {
|
|
27
|
-
const spin = spinner("Cargando ramas...");
|
|
28
|
-
const ramas = await git.branchLocal();
|
|
29
|
-
spin.stop();
|
|
30
|
-
const actual = ramas.current;
|
|
31
|
-
const opciones = ramas.all
|
|
32
|
-
.filter(r => r !== actual)
|
|
33
|
-
.map(r => ({ name: r, value: r }));
|
|
34
|
-
if (opciones.length === 0) {
|
|
35
|
-
console.log(c.aviso("\n No hay otras ramas para fusionar.\n"));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const origen = await select({
|
|
39
|
-
message: `Fusionar en "${c.rama(actual)}" ← ¿desde qué rama?`,
|
|
40
|
-
choices: opciones,
|
|
41
|
-
});
|
|
42
|
-
const modo = await select({
|
|
43
|
-
message: "Modo de merge:",
|
|
44
|
-
choices: [
|
|
45
|
-
{ name: "Normal (crea commit de merge)", value: "normal" },
|
|
46
|
-
{ name: "No fast-forward (fuerza merge commit siempre)", value: "noff" },
|
|
47
|
-
{ name: "Squash (aplana todos los commits en uno)", value: "squash" },
|
|
48
|
-
],
|
|
49
|
-
});
|
|
50
|
-
separador();
|
|
51
|
-
console.log(` ${c.rama(origen)} → ${c.rama(actual)}`);
|
|
52
|
-
separador();
|
|
53
|
-
const confirmar = await confirm({ message: "¿Confirmar merge?", default: true });
|
|
54
|
-
if (!confirmar) {
|
|
55
|
-
console.log(c.aviso(" Merge cancelado.\n"));
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const spinMerge = spinner(`Fusionando ${origen} en ${actual}...`);
|
|
59
|
-
try {
|
|
60
|
-
switch (modo) {
|
|
61
|
-
case "noff":
|
|
62
|
-
await git.merge([origen, "--no-ff"]);
|
|
63
|
-
break;
|
|
64
|
-
case "squash":
|
|
65
|
-
await git.merge([origen, "--squash"]);
|
|
66
|
-
spinMerge.stop();
|
|
67
|
-
console.log(c.exito(`\n ✓ Squash merge listo. Debes hacer commit manual.\n`));
|
|
68
|
-
return;
|
|
69
|
-
default:
|
|
70
|
-
await git.merge([origen]);
|
|
71
|
-
}
|
|
72
|
-
spinMerge.stop();
|
|
73
|
-
console.log(c.exito(`\n ✓ Merge completado: ${origen} → ${actual}\n`));
|
|
74
|
-
}
|
|
75
|
-
catch (err) {
|
|
76
|
-
spinMerge.stop();
|
|
77
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
78
|
-
console.log(c.error(`\n ✗ Error en merge (posibles conflictos):\n ${msg}\n`));
|
|
79
|
-
console.log(c.aviso(" Usa 'Abortar merge en curso' si deseas revertir.\n"));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// ── Rebase ────────────────────────────────────────────────────────────────────
|
|
83
|
-
async function hacerRebase() {
|
|
84
|
-
const spin = spinner("Cargando ramas...");
|
|
85
|
-
const ramas = await git.branchLocal();
|
|
86
|
-
spin.stop();
|
|
87
|
-
const actual = ramas.current;
|
|
88
|
-
const opciones = ramas.all
|
|
89
|
-
.filter(r => r !== actual)
|
|
90
|
-
.map(r => ({ name: r, value: r }));
|
|
91
|
-
if (opciones.length === 0) {
|
|
92
|
-
console.log(c.aviso("\n No hay otras ramas para hacer rebase.\n"));
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const base = await select({
|
|
96
|
-
message: `Rebasar "${c.rama(actual)}" sobre ¿qué rama?`,
|
|
97
|
-
choices: opciones,
|
|
98
|
-
});
|
|
99
|
-
console.log();
|
|
100
|
-
console.log(c.aviso(" ⚠ El rebase reescribe el historial de commits."));
|
|
101
|
-
console.log(c.aviso(" No lo uses en ramas compartidas/remotas."));
|
|
102
|
-
separador();
|
|
103
|
-
const confirmar = await confirm({ message: `¿Confirmar rebase sobre ${base}?`, default: false });
|
|
104
|
-
if (!confirmar) {
|
|
105
|
-
console.log(c.aviso(" Rebase cancelado.\n"));
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const spinRebase = spinner(`Aplicando rebase sobre ${base}...`);
|
|
109
|
-
try {
|
|
110
|
-
await git.rebase([base]);
|
|
111
|
-
spinRebase.stop();
|
|
112
|
-
console.log(c.exito(`\n ✓ Rebase completado sobre ${c.rama(base)}\n`));
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
spinRebase.stop();
|
|
116
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
117
|
-
console.log(c.error(`\n ✗ Conflictos durante rebase:\n ${msg}\n`));
|
|
118
|
-
console.log(c.aviso(" Resuelve conflictos manualmente o usa 'Abortar rebase'.\n"));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
// ── Abortar merge ─────────────────────────────────────────────────────────────
|
|
122
|
-
async function abortarMerge() {
|
|
123
|
-
const confirmar = await confirm({ message: "¿Abortar el merge en curso?", default: false });
|
|
124
|
-
if (!confirmar)
|
|
125
|
-
return;
|
|
126
|
-
const spin = spinner("Abortando merge...");
|
|
127
|
-
try {
|
|
128
|
-
await git.merge(["--abort"]);
|
|
129
|
-
spin.stop();
|
|
130
|
-
console.log(c.exito("\n ✓ Merge abortado. Estado restaurado.\n"));
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
spin.stop();
|
|
134
|
-
console.log(c.error("\n ✗ No hay merge en curso o falló el abort.\n"));
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// ── Abortar rebase ─────────────────────────────────────────────────────────────
|
|
138
|
-
async function abortarRebase() {
|
|
139
|
-
const confirmar = await confirm({ message: "¿Abortar el rebase en curso?", default: false });
|
|
140
|
-
if (!confirmar)
|
|
141
|
-
return;
|
|
142
|
-
const spin = spinner("Abortando rebase...");
|
|
143
|
-
try {
|
|
144
|
-
await git.rebase(["--abort"]);
|
|
145
|
-
spin.stop();
|
|
146
|
-
console.log(c.exito("\n ✓ Rebase abortado. Estado restaurado.\n"));
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
spin.stop();
|
|
150
|
-
console.log(c.error("\n ✗ No hay rebase en curso o falló el abort.\n"));
|
|
151
|
-
}
|
|
152
|
-
}
|
package/dist/ramas.js
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
-
import { select, input, confirm } from "@inquirer/prompts";
|
|
3
|
-
// ── Menú de ramas ─────────────────────────────────────────────────────────────
|
|
4
|
-
export async function menuRamas() {
|
|
5
|
-
if (!(await verificarRepo()))
|
|
6
|
-
return;
|
|
7
|
-
const accion = await select({
|
|
8
|
-
message: "Ramas — ¿qué deseas hacer?",
|
|
9
|
-
choices: [
|
|
10
|
-
{ name: "Listar todas las ramas", value: "listar" },
|
|
11
|
-
{ name: "Cambiar de rama (checkout)", value: "cambiar" },
|
|
12
|
-
{ name: "Crear nueva rama", value: "crear" },
|
|
13
|
-
{ name: "Crear y cambiar a nueva rama", value: "crearCambiar" },
|
|
14
|
-
{ name: "Eliminar rama", value: "eliminar" },
|
|
15
|
-
{ name: "Renombrar rama actual", value: "renombrar" },
|
|
16
|
-
{ name: "← Volver al menú principal", value: "volver" },
|
|
17
|
-
],
|
|
18
|
-
});
|
|
19
|
-
switch (accion) {
|
|
20
|
-
case "listar": return listarRamas();
|
|
21
|
-
case "cambiar": return cambiarRama();
|
|
22
|
-
case "crear": return crearRama(false);
|
|
23
|
-
case "crearCambiar": return crearRama(true);
|
|
24
|
-
case "eliminar": return eliminarRama();
|
|
25
|
-
case "renombrar": return renombrarRama();
|
|
26
|
-
case "volver": return;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
// ── Listar ramas ──────────────────────────────────────────────────────────────
|
|
30
|
-
async function listarRamas() {
|
|
31
|
-
const spin = spinner("Cargando ramas...");
|
|
32
|
-
const ramas = await git.branch(["-a"]);
|
|
33
|
-
spin.stop();
|
|
34
|
-
console.log();
|
|
35
|
-
console.log(c.titulo(" 🌿 Ramas del repositorio"));
|
|
36
|
-
separador();
|
|
37
|
-
ramas.all.forEach(rama => {
|
|
38
|
-
const esActual = rama === ramas.current;
|
|
39
|
-
const esRemota = rama.startsWith("remotes/");
|
|
40
|
-
const nombre = rama.replace("remotes/", "");
|
|
41
|
-
if (esActual) {
|
|
42
|
-
console.log(c.exito(` ◉ ${nombre}`) + c.tenue(" ← actual"));
|
|
43
|
-
}
|
|
44
|
-
else if (esRemota) {
|
|
45
|
-
console.log(c.tenue(` ○ ${nombre}`) + c.info(" (remota)"));
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
console.log(c.titulo(` ○ ${nombre}`));
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
separador();
|
|
52
|
-
console.log();
|
|
53
|
-
}
|
|
54
|
-
// ── Cambiar de rama ───────────────────────────────────────────────────────────
|
|
55
|
-
async function cambiarRama() {
|
|
56
|
-
const spin = spinner("Cargando ramas locales...");
|
|
57
|
-
const ramas = await git.branchLocal();
|
|
58
|
-
spin.stop();
|
|
59
|
-
const opciones = ramas.all
|
|
60
|
-
.filter(r => r !== ramas.current)
|
|
61
|
-
.map(r => ({ name: r, value: r }));
|
|
62
|
-
if (opciones.length === 0) {
|
|
63
|
-
console.log(c.aviso("\n No hay otras ramas locales disponibles.\n"));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const destino = await select({
|
|
67
|
-
message: "Selecciona la rama de destino:",
|
|
68
|
-
choices: opciones,
|
|
69
|
-
});
|
|
70
|
-
const spinSwitch = spinner(`Cambiando a ${destino}...`);
|
|
71
|
-
try {
|
|
72
|
-
await git.checkout(destino);
|
|
73
|
-
spinSwitch.stop();
|
|
74
|
-
console.log(c.exito(`\n ✓ Ahora estás en: ${c.rama(destino)}\n`));
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
spinSwitch.stop();
|
|
78
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
79
|
-
console.log(c.error(`\n ✗ Error al cambiar de rama:\n ${msg}\n`));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// ── Crear rama (con opción de checkout inmediato) ─────────────────────────────
|
|
83
|
-
async function crearRama(cambiarAlCrear) {
|
|
84
|
-
const nombre = await input({
|
|
85
|
-
message: "Nombre de la nueva rama:",
|
|
86
|
-
validate: (v) => {
|
|
87
|
-
if (!v.trim())
|
|
88
|
-
return "El nombre no puede estar vacío.";
|
|
89
|
-
if (/\s/.test(v))
|
|
90
|
-
return "El nombre no puede tener espacios.";
|
|
91
|
-
return true;
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
const spinCrear = spinner(`Creando rama ${nombre}...`);
|
|
95
|
-
try {
|
|
96
|
-
if (cambiarAlCrear) {
|
|
97
|
-
await git.checkoutLocalBranch(nombre);
|
|
98
|
-
spinCrear.stop();
|
|
99
|
-
console.log(c.exito(`\n ✓ Rama creada y activa: ${c.rama(nombre)}\n`));
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
await git.branch([nombre]);
|
|
103
|
-
spinCrear.stop();
|
|
104
|
-
console.log(c.exito(`\n ✓ Rama creada: ${c.rama(nombre)}\n`));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
catch (err) {
|
|
108
|
-
spinCrear.stop();
|
|
109
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
110
|
-
console.log(c.error(`\n ✗ Error al crear rama:\n ${msg}\n`));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// ── Eliminar rama ─────────────────────────────────────────────────────────────
|
|
114
|
-
async function eliminarRama() {
|
|
115
|
-
const spin = spinner("Cargando ramas locales...");
|
|
116
|
-
const ramas = await git.branchLocal();
|
|
117
|
-
spin.stop();
|
|
118
|
-
const opciones = ramas.all
|
|
119
|
-
.filter(r => r !== ramas.current)
|
|
120
|
-
.map(r => ({ name: r, value: r }));
|
|
121
|
-
if (opciones.length === 0) {
|
|
122
|
-
console.log(c.aviso("\n No hay ramas locales para eliminar (no puedes eliminar la actual).\n"));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const objetivo = await select({
|
|
126
|
-
message: "¿Qué rama deseas eliminar?",
|
|
127
|
-
choices: opciones,
|
|
128
|
-
});
|
|
129
|
-
const forzar = await confirm({
|
|
130
|
-
message: `¿Forzar eliminación de "${objetivo}"? (--force)`,
|
|
131
|
-
default: false,
|
|
132
|
-
});
|
|
133
|
-
const confirmar = await confirm({
|
|
134
|
-
message: c.error(`¿Seguro que deseas eliminar "${objetivo}"?`),
|
|
135
|
-
default: false,
|
|
136
|
-
});
|
|
137
|
-
if (!confirmar) {
|
|
138
|
-
console.log(c.aviso(" Operación cancelada.\n"));
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const spinDel = spinner(`Eliminando rama ${objetivo}...`);
|
|
142
|
-
try {
|
|
143
|
-
await git.deleteLocalBranch(objetivo, forzar);
|
|
144
|
-
spinDel.stop();
|
|
145
|
-
console.log(c.exito(`\n ✓ Rama eliminada: ${objetivo}\n`));
|
|
146
|
-
}
|
|
147
|
-
catch (err) {
|
|
148
|
-
spinDel.stop();
|
|
149
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
150
|
-
console.log(c.error(`\n ✗ Error:\n ${msg}\n`));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
// ── Renombrar rama actual ─────────────────────────────────────────────────────
|
|
154
|
-
async function renombrarRama() {
|
|
155
|
-
const actual = (await git.branchLocal()).current;
|
|
156
|
-
console.log(c.info(`\n Rama actual: ${c.rama(actual)}`));
|
|
157
|
-
const nuevoNombre = await input({
|
|
158
|
-
message: "Nuevo nombre:",
|
|
159
|
-
validate: (v) => {
|
|
160
|
-
if (!v.trim())
|
|
161
|
-
return "El nombre no puede estar vacío.";
|
|
162
|
-
if (/\s/.test(v))
|
|
163
|
-
return "Sin espacios.";
|
|
164
|
-
return true;
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
const spinRen = spinner("Renombrando...");
|
|
168
|
-
try {
|
|
169
|
-
await git.branch(["-m", actual, nuevoNombre]);
|
|
170
|
-
spinRen.stop();
|
|
171
|
-
console.log(c.exito(`\n ✓ Renombrada: ${actual} → ${c.rama(nuevoNombre)}\n`));
|
|
172
|
-
}
|
|
173
|
-
catch (err) {
|
|
174
|
-
spinRen.stop();
|
|
175
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
176
|
-
console.log(c.error(`\n ✗ Error:\n ${msg}\n`));
|
|
177
|
-
}
|
|
178
|
-
}
|