@asaro/git-cli 1.0.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/README.md +189 -0
- package/dist/adapters/git/SimpleGitAdapter.js +126 -0
- package/dist/adapters/github/GitHubAPIAdapter.js +243 -0
- package/dist/adapters/storage/FileSecureStorageAdapter.js +67 -0
- package/dist/adapters/ui/ConsoleUIAdapter.js +91 -0
- package/dist/commands/BaseCommand.js +11 -0
- package/dist/commands/BranchesCommand.js +152 -0
- package/dist/commands/Command.js +1 -0
- package/dist/commands/CommitCommand.js +85 -0
- package/dist/commands/IntegrationCommand.js +151 -0
- package/dist/commands/LogCommand.js +42 -0
- package/dist/commands/PullCommand.js +55 -0
- package/dist/commands/PushCommand.js +227 -0
- package/dist/commands/RepositoryCommand.js +268 -0
- package/dist/commands/StashCommand.js +140 -0
- package/dist/commands/StatusCommand.js +66 -0
- package/dist/commands/github/BaseGitHubCommand.js +17 -0
- package/dist/commands/github/GitHubAuthCommand.js +48 -0
- package/dist/commands/github/GitHubMenuCommand.js +57 -0
- package/dist/commands/github/GitHubReposCommand.js +210 -0
- package/dist/commands/github/GitHubWorkflowsCommand.js +243 -0
- package/dist/commit.js +98 -0
- package/dist/core/errors/GitError.js +19 -0
- package/dist/core/errors/GitHubError.js +36 -0
- package/dist/core/ports/GitHubPort.js +1 -0
- package/dist/core/ports/GitPort.js +1 -0
- package/dist/core/ports/SecureStoragePort.js +1 -0
- package/dist/core/ports/UIPort.js +1 -0
- package/dist/core/services/GitHubService.js +95 -0
- package/dist/core/services/GitService.js +44 -0
- package/dist/estado.js +97 -0
- package/dist/factories/CommandFactory.js +51 -0
- package/dist/index.js +71 -0
- package/dist/integracion.js +152 -0
- package/dist/ramas.js +178 -0
- package/dist/remoto.js +129 -0
- package/dist/repositorio.js +159 -0
- package/dist/stash.js +169 -0
- package/dist/utils.js +49 -0
- package/package.json +53 -0
|
@@ -0,0 +1,152 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
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
|
+
}
|
package/dist/remoto.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
+
import { select, confirm } from "@inquirer/prompts";
|
|
3
|
+
// ── Push ─────────────────────────────────────────────────────────────────────
|
|
4
|
+
export async function hacerPush() {
|
|
5
|
+
if (!(await verificarRepo()))
|
|
6
|
+
return;
|
|
7
|
+
const spin = spinner("Verificando estado...");
|
|
8
|
+
const status = await git.status();
|
|
9
|
+
const remotos = await git.getRemotes(true);
|
|
10
|
+
spin.stop();
|
|
11
|
+
if (remotos.length === 0) {
|
|
12
|
+
console.log(c.error("\n ✗ No hay remotos configurados en este repositorio.\n"));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const rama = status.current ?? "main";
|
|
16
|
+
console.log();
|
|
17
|
+
console.log(c.titulo(" ↑ Subir cambios (push)"));
|
|
18
|
+
separador();
|
|
19
|
+
console.log(` Rama actual: ${c.rama(rama)}`);
|
|
20
|
+
if (status.ahead > 0) {
|
|
21
|
+
console.log(c.aviso(` ${status.ahead} commit(s) pendientes de subir`));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log(c.tenue(" No hay commits nuevos para subir."));
|
|
25
|
+
}
|
|
26
|
+
separador();
|
|
27
|
+
// Seleccionar remoto si hay más de uno
|
|
28
|
+
let remoto = "origin";
|
|
29
|
+
if (remotos.length > 1) {
|
|
30
|
+
remoto = await select({
|
|
31
|
+
message: "¿A qué remoto hacer push?",
|
|
32
|
+
choices: remotos.map(r => ({ name: r.name, value: r.name })),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const modo = await select({
|
|
36
|
+
message: "Modo de push:",
|
|
37
|
+
choices: [
|
|
38
|
+
{ name: `Subir rama actual (${rama})`, value: "normal" },
|
|
39
|
+
{ name: "Subir y crear rama en remoto (--set-upstream)", value: "upstream" },
|
|
40
|
+
{ name: "Forzar push (--force-with-lease)", value: "force" },
|
|
41
|
+
{ name: "Subir todos los tags", value: "tags" },
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
const confirmar = await confirm({ message: `¿Confirmar push a ${remoto}/${rama}?`, default: true });
|
|
45
|
+
if (!confirmar) {
|
|
46
|
+
console.log(c.aviso(" Push cancelado.\n"));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const spinPush = spinner("Subiendo cambios...");
|
|
50
|
+
try {
|
|
51
|
+
switch (modo) {
|
|
52
|
+
case "upstream":
|
|
53
|
+
await git.push([remoto, rama, "--set-upstream"]);
|
|
54
|
+
break;
|
|
55
|
+
case "force":
|
|
56
|
+
await git.push([remoto, rama, "--force-with-lease"]);
|
|
57
|
+
break;
|
|
58
|
+
case "tags":
|
|
59
|
+
await git.push([remoto, "--tags"]);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
await git.push(remoto, rama);
|
|
63
|
+
}
|
|
64
|
+
spinPush.stop();
|
|
65
|
+
console.log(c.exito(`\n ✓ Push exitoso → ${remoto}/${rama}\n`));
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
spinPush.stop();
|
|
69
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
70
|
+
console.log(c.error(`\n ✗ Error en push:\n ${msg}\n`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ── Pull ─────────────────────────────────────────────────────────────────────
|
|
74
|
+
export async function hacerPull() {
|
|
75
|
+
if (!(await verificarRepo()))
|
|
76
|
+
return;
|
|
77
|
+
const spin = spinner("Verificando estado...");
|
|
78
|
+
const status = await git.status();
|
|
79
|
+
const remotos = await git.getRemotes(true);
|
|
80
|
+
spin.stop();
|
|
81
|
+
if (remotos.length === 0) {
|
|
82
|
+
console.log(c.error("\n ✗ No hay remotos configurados.\n"));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const rama = status.current ?? "main";
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(c.titulo(" ↓ Bajar cambios (pull)"));
|
|
88
|
+
separador();
|
|
89
|
+
console.log(` Rama actual: ${c.rama(rama)}`);
|
|
90
|
+
if (status.behind > 0) {
|
|
91
|
+
console.log(c.aviso(` ${status.behind} commit(s) disponibles en el remoto`));
|
|
92
|
+
}
|
|
93
|
+
separador();
|
|
94
|
+
let remoto = "origin";
|
|
95
|
+
if (remotos.length > 1) {
|
|
96
|
+
remoto = await select({
|
|
97
|
+
message: "¿Desde qué remoto hacer pull?",
|
|
98
|
+
choices: remotos.map(r => ({ name: r.name, value: r.name })),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const estrategia = await select({
|
|
102
|
+
message: "Estrategia de integración:",
|
|
103
|
+
choices: [
|
|
104
|
+
{ name: "Merge (por defecto)", value: "merge" },
|
|
105
|
+
{ name: "Rebase (historial más limpio)", value: "rebase" },
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
const confirmar = await confirm({ message: `¿Confirmar pull desde ${remoto}/${rama}?`, default: true });
|
|
109
|
+
if (!confirmar) {
|
|
110
|
+
console.log(c.aviso(" Pull cancelado.\n"));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const spinPull = spinner("Bajando cambios...");
|
|
114
|
+
try {
|
|
115
|
+
if (estrategia === "rebase") {
|
|
116
|
+
await git.pull(remoto, rama, { "--rebase": "true" });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
await git.pull(remoto, rama);
|
|
120
|
+
}
|
|
121
|
+
spinPull.stop();
|
|
122
|
+
console.log(c.exito(`\n ✓ Pull exitoso desde ${remoto}/${rama}\n`));
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
spinPull.stop();
|
|
126
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
127
|
+
console.log(c.error(`\n ✗ Error en pull:\n ${msg}\n`));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
+
import { select, input, confirm } from "@inquirer/prompts";
|
|
3
|
+
import { simpleGit } from "simple-git";
|
|
4
|
+
import path from "path";
|
|
5
|
+
// ── Menú Clone / Init ─────────────────────────────────────────────────────────
|
|
6
|
+
export async function menuOrigenRepo() {
|
|
7
|
+
const accion = await select({
|
|
8
|
+
message: "Repositorio — ¿qué deseas hacer?",
|
|
9
|
+
choices: [
|
|
10
|
+
{ name: "Clonar repositorio remoto", value: "clonar" },
|
|
11
|
+
{ name: "Inicializar nuevo repositorio", value: "init" },
|
|
12
|
+
{ name: "Ver remotos configurados", value: "remotos" },
|
|
13
|
+
{ name: "Agregar remoto", value: "agregarRemoto" },
|
|
14
|
+
{ name: "← Volver al menú principal", value: "volver" },
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
switch (accion) {
|
|
18
|
+
case "clonar": return clonarRepo();
|
|
19
|
+
case "init": return inicializarRepo();
|
|
20
|
+
case "remotos": return verRemotos();
|
|
21
|
+
case "agregarRemoto": return agregarRemoto();
|
|
22
|
+
case "volver": return;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// ── Clonar ────────────────────────────────────────────────────────────────────
|
|
26
|
+
async function clonarRepo() {
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(c.titulo(" 📥 Clonar repositorio"));
|
|
29
|
+
separador();
|
|
30
|
+
const url = await input({
|
|
31
|
+
message: "URL del repositorio (HTTPS o SSH):",
|
|
32
|
+
validate: (v) => v.trim().length > 0 || "La URL no puede estar vacía.",
|
|
33
|
+
});
|
|
34
|
+
const nombreSugerido = url.split("/").pop()?.replace(".git", "") ?? "repo";
|
|
35
|
+
const destino = await input({
|
|
36
|
+
message: "Carpeta de destino:",
|
|
37
|
+
default: nombreSugerido,
|
|
38
|
+
validate: (v) => v.trim().length > 0 || "El nombre no puede estar vacío.",
|
|
39
|
+
});
|
|
40
|
+
const rama = await input({
|
|
41
|
+
message: "Rama a clonar (dejar vacío para rama por defecto):",
|
|
42
|
+
default: "",
|
|
43
|
+
});
|
|
44
|
+
const cwd = process.cwd();
|
|
45
|
+
const destinoAbsoluto = path.join(cwd, destino);
|
|
46
|
+
separador();
|
|
47
|
+
console.log(` URL: ${c.info(url)}`);
|
|
48
|
+
console.log(` Carpeta: ${c.archivo(destinoAbsoluto)}`);
|
|
49
|
+
if (rama.trim())
|
|
50
|
+
console.log(` Rama: ${c.rama(rama.trim())}`);
|
|
51
|
+
separador();
|
|
52
|
+
const confirmar = await confirm({ message: "¿Confirmar clonación?", default: true });
|
|
53
|
+
if (!confirmar) {
|
|
54
|
+
console.log(c.aviso(" Clonación cancelada.\n"));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const spinClone = spinner("Clonando repositorio...");
|
|
58
|
+
try {
|
|
59
|
+
const g = simpleGit(cwd);
|
|
60
|
+
const opciones = [];
|
|
61
|
+
if (rama.trim()) {
|
|
62
|
+
opciones.push("-b", rama.trim());
|
|
63
|
+
}
|
|
64
|
+
await g.clone(url, destino, opciones);
|
|
65
|
+
spinClone.stop();
|
|
66
|
+
console.log(c.exito(`\n ✓ Repositorio clonado en: ${c.archivo(destinoAbsoluto)}\n`));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
spinClone.stop();
|
|
70
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
71
|
+
console.log(c.error(`\n ✗ Error al clonar:\n ${msg}\n`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ── Init ──────────────────────────────────────────────────────────────────────
|
|
75
|
+
async function inicializarRepo() {
|
|
76
|
+
console.log();
|
|
77
|
+
console.log(c.titulo(" 🆕 Inicializar repositorio"));
|
|
78
|
+
separador();
|
|
79
|
+
const esActual = await select({
|
|
80
|
+
message: "¿Dónde inicializar?",
|
|
81
|
+
choices: [
|
|
82
|
+
{ name: `Directorio actual (${process.cwd()})`, value: "actual" },
|
|
83
|
+
{ name: "Nueva carpeta", value: "nueva" },
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
let destino = process.cwd();
|
|
87
|
+
if (esActual === "nueva") {
|
|
88
|
+
const nombreCarpeta = await input({
|
|
89
|
+
message: "Nombre de la nueva carpeta:",
|
|
90
|
+
validate: (v) => v.trim().length > 0 || "El nombre no puede estar vacío.",
|
|
91
|
+
});
|
|
92
|
+
destino = path.join(process.cwd(), nombreCarpeta);
|
|
93
|
+
}
|
|
94
|
+
const ramaInicial = await input({
|
|
95
|
+
message: "Nombre de la rama inicial:",
|
|
96
|
+
default: "main",
|
|
97
|
+
});
|
|
98
|
+
const confirmar = await confirm({ message: `¿Inicializar repo en: ${c.archivo(destino)}?`, default: true });
|
|
99
|
+
if (!confirmar)
|
|
100
|
+
return;
|
|
101
|
+
const spinInit = spinner("Inicializando...");
|
|
102
|
+
try {
|
|
103
|
+
const g = simpleGit(destino);
|
|
104
|
+
await g.init({ "--initial-branch": ramaInicial.trim() || "main" });
|
|
105
|
+
spinInit.stop();
|
|
106
|
+
console.log(c.exito(`\n ✓ Repositorio inicializado en: ${c.archivo(destino)}\n`));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
spinInit.stop();
|
|
110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
111
|
+
console.log(c.error(`\n ✗ Error al inicializar:\n ${msg}\n`));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ── Ver remotos ───────────────────────────────────────────────────────────────
|
|
115
|
+
async function verRemotos() {
|
|
116
|
+
if (!(await verificarRepo()))
|
|
117
|
+
return;
|
|
118
|
+
const spin = spinner("Cargando remotos...");
|
|
119
|
+
const remotos = await git.getRemotes(true);
|
|
120
|
+
spin.stop();
|
|
121
|
+
console.log();
|
|
122
|
+
console.log(c.titulo(" 🌐 Remotos configurados"));
|
|
123
|
+
separador();
|
|
124
|
+
if (remotos.length === 0) {
|
|
125
|
+
console.log(c.tenue(" No hay remotos configurados."));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
remotos.forEach(r => {
|
|
129
|
+
console.log(` ${c.info(r.name.padEnd(10))} ${c.tenue(r.refs.fetch ?? "")}`);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
separador();
|
|
133
|
+
console.log();
|
|
134
|
+
}
|
|
135
|
+
// ── Agregar remoto ─────────────────────────────────────────────────────────────
|
|
136
|
+
async function agregarRemoto() {
|
|
137
|
+
if (!(await verificarRepo()))
|
|
138
|
+
return;
|
|
139
|
+
const nombre = await input({
|
|
140
|
+
message: "Nombre del remoto:",
|
|
141
|
+
default: "origin",
|
|
142
|
+
validate: (v) => v.trim().length > 0 || "Nombre requerido.",
|
|
143
|
+
});
|
|
144
|
+
const url = await input({
|
|
145
|
+
message: "URL del remoto:",
|
|
146
|
+
validate: (v) => v.trim().length > 0 || "URL requerida.",
|
|
147
|
+
});
|
|
148
|
+
const spinRem = spinner("Agregando remoto...");
|
|
149
|
+
try {
|
|
150
|
+
await git.addRemote(nombre, url);
|
|
151
|
+
spinRem.stop();
|
|
152
|
+
console.log(c.exito(`\n ✓ Remoto "${nombre}" agregado → ${url}\n`));
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
spinRem.stop();
|
|
156
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
157
|
+
console.log(c.error(`\n ✗ Error:\n ${msg}\n`));
|
|
158
|
+
}
|
|
159
|
+
}
|