Cerramos el módulo 1 con el portátil de Ana preparado: Git instalado, la identidad configurada, init.defaultBranch valiendo main y el editor listo. Todo el andamiaje estaba montado, pero gestor-tareas seguía siendo lo que era el primer día: una carpeta normal y corriente con cuatro ficheros dentro. Ni historial, ni versiones, ni forma de volver atrás.
En esta lección damos el paso que lo cambia todo. Con un único comando, git init, esa carpeta pasa a ser un repositorio: Git empieza a observarla, aparece el directorio .git/ con la base de datos de objetos que estudiamos en El Modelo de Datos de Git, y a partir de ahí cada cambio puede quedar registrado para siempre.
Veremos qué crea exactamente git init, qué dice git status en un repositorio recién nacido (y por qué habla de "no commits yet"), cómo se crea la primera confirmación del proyecto, en qué se diferencia arrancar de cero de convertir un proyecto que ya existe, y cómo deshacer un git init lanzado por error en el sitio equivocado —algo que le pasa a casi todo el mundo alguna vez.
Contenido
- El punto de partida: la carpeta de Ana
git init: el comando que crea el repositorio- Qué crea exactamente dentro de
.git/ git statusen un repositorio recién creado- Qué significa "No commits yet"
- La primera confirmación del proyecto
- Convertir un proyecto existente frente a empezar de cero
- Repositorios bare: qué son y para qué sirven
- Cómo deshacer un
git initaccidental - Comprobaciones útiles antes de seguir
- El punto de partida: la carpeta de Ana
Ana trabaja en Ubuntu y tiene su proyecto en ~/proyectos/gestor-tareas. Lo ha escrito durante un par de tardes y contiene cuatro ficheros:
total 16 -rw-rw-r-- 1 ana ana 486 jul 20 18:12 app.js -rw-rw-r-- 1 ana ana 312 jul 20 17:55 estilos.css -rw-rw-r-- 1 ana ana 602 jul 20 18:04 index.html -rw-rw-r-- 1 ana ana 198 jul 20 18:20 README.md
El contenido es sencillo. index.html monta la página:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Gestor de Tareas</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<h1>Gestor de Tareas</h1>
<form id="form-tarea">
<input type="text" id="campo-tarea" placeholder="¿Qué hay que hacer?">
<button type="submit">Añadir</button>
</form>
<ul id="lista-tareas"></ul>
<script src="app.js"></script>
</body>
</html>Y app.js contiene la lógica mínima para añadir tareas:
// gestor-tareas — lógica principal
const tareas = [];
function anadirTarea(texto) {
tareas.push({ id: Date.now(), texto: texto, hecha: false });
pintarLista();
}
function pintarLista() {
const lista = document.querySelector('#lista-tareas');
lista.innerHTML = '';
for (const tarea of tareas) {
const li = document.createElement('li');
li.textContent = tarea.texto;
lista.appendChild(li);
}
}
document.querySelector('#form-tarea').addEventListener('submit', function (evento) {
evento.preventDefault();
const campo = document.querySelector('#campo-tarea');
if (campo.value.trim() !== '') {
anadirTarea(campo.value.trim());
campo.value = '';
}
});Estos ficheros nos acompañarán durante todo el curso. Fíjate en un detalle que importará más adelante: no hay ninguna carpeta oculta. Comprobémoslo:
Solo están . (el directorio actual) y .. (el padre). Ni rastro de .git. Esto es lo que Git llama, con precisión, un directorio que no está bajo control de versiones. Si Ana borrase app.js ahora mismo, no habría nada que hacer.
git init: el comando que crea el repositorio
git init: el comando que crea el repositorioAna se sitúa dentro de la carpeta del proyecto y ejecuta:
Ya está. Eso es todo. Analicemos el mensaje con calma, porque cada palabra dice algo:
Initialized: se ha creado la estructura desde cero.empty: el repositorio está vacío en el sentido de que no contiene ninguna confirmación. Ojo: los cuatro ficheros de Ana siguen ahí intactos. "Vacío" se refiere al historial, no a la carpeta.- La ruta
/home/ana/proyectos/gestor-tareas/.git/: te dice exactamente dónde ha quedado el repositorio. Merece la pena leerla siempre, porque es la forma más rápida de detectar que lo has ejecutado en el sitio equivocado.
Las dos formas de invocarlo
| Forma | Qué hace | Cuándo usarla |
|---|---|---|
git init |
Convierte el directorio actual en repositorio | Ya tienes una carpeta con ficheros (el caso de Ana) |
git init <nombre> |
Crea el directorio <nombre> si no existe y lo inicializa |
Empiezas un proyecto desde cero |
La segunda forma ahorra un paso. Si Ana fuera a empezar un proyecto nuevo llamado notas-equipo:
Git ha creado la carpeta y la ha inicializado, pero no ha puesto nada dentro: el proyecto está por escribir.
Opciones que conviene conocer
# Forzar el nombre de la rama inicial en este repositorio concreto
git init --initial-branch=main
git init -b main # forma abreviada
# Inicializar con salida mínima
git init --quietAna no necesita -b main porque ya fijó init.defaultBranch=main en su configuración global en Configuración Inicial. Pero si trabajas en un equipo donde no todos lo tienen configurado, git init -b main garantiza el mismo punto de partida para todo el mundo.
Nota sobre versiones antiguas. La opción
-b/--initial-branchexiste desde Git 2.28. En versiones anteriores la rama inicial siempre se llamabamastery no había forma de cambiarla en el momento de crear el repositorio. Si tugit --versionestá por debajo de 2.28, este es un buen motivo para actualizar.
- Qué crea exactamente dentro de
.git/
.git/Ahora sí, veamos qué ha aparecido:
El único cambio visible es la carpeta .git. Miremos dentro:
Esto conecta directamente con lo que vimos en El Modelo de Datos de Git. Repasemos qué es cada pieza en un repositorio recién inicializado:
| Elemento | Qué es | Estado recién creado |
|---|---|---|
HEAD |
Referencia a la rama actual | Apunta a refs/heads/main, que todavía no existe |
config |
Configuración de nivel --local |
Solo unos pocos valores por defecto |
description |
Nombre del repositorio para GitWeb | Texto de relleno; irrelevante hoy |
objects/ |
La base de datos de objetos | Vacía: sin blobs, sin trees, sin commits |
refs/ |
Ramas (heads/) y etiquetas (tags/) |
Ambos subdirectorios vacíos |
hooks/ |
Scripts que se disparan en ciertos eventos | Solo ejemplos desactivados (.sample) |
info/ |
Información auxiliar, como exclude |
Prácticamente vacío |
branches/ |
Directorio heredado, en desuso | Vacío; puedes ignorarlo |
Fíjate en lo que no está: no hay fichero index. El área de preparación es un fichero binario (.git/index) que Git crea la primera vez que preparas algo. Antes de eso, sencillamente no existe.
Mirando los ficheros clave
HEAD es un fichero de texto de una sola línea:
Dice: "la rama actual es main". Pero ahora mismo esa rama es una promesa, no un hecho:
El directorio está vacío. main no existe como fichero de referencia porque una rama es, literalmente, un fichero que contiene el hash de una confirmación, y todavía no hay ninguna confirmación a la que apuntar. Esta situación —HEAD apuntando a una rama inexistente— se llama rama huérfana o unborn branch, y se resuelve sola en cuanto se cree el primer commit.
El config local también es corto:
Cuatro valores técnicos y nada más. Recuerda de Configurando Git que este fichero es el nivel --local, el de mayor precedencia: la identidad de Ana no está aquí porque la puso en --global, y desde allí aplica igual.
Y la base de datos de objetos está estrictamente vacía:
Dos subdirectorios vacíos esperando el primer blob. Esto ilustra bien la idea de fondo: git init no guarda ninguno de tus ficheros. Solo prepara el almacén. Guardar es cosa de git add y git commit.
git status en un repositorio recién creado
git status en un repositorio recién creadogit status es el comando que más veces escribirás en tu vida. Veamos qué dice ahora:
On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) README.md app.js estilos.css index.html nothing added to commit but untracked files present (use "git add" to track)
Cuatro bloques de información, y todos importan:
On branch main— en qué rama estás. Coincide con lo que leímos en.git/HEAD.No commits yet— el historial está vacío. Lo desarrollamos en el apartado siguiente.Untracked files— los ficheros sin seguimiento. Git los ve en el directorio de trabajo, pero nunca los ha registrado, así que no forman parte del proyecto todavía. Es el estado que presentamos en Terminología Básica de Git.- La última línea — un resumen: no hay nada preparado, pero sí ficheros sin seguimiento.
Observa el detalle de que Git sugiere el comando siguiente entre paréntesis: use "git add <file>...". Git es insistentemente pedagógico en su salida. Leer esas sugerencias en lugar de saltárselas acorta muchísimo la curva de aprendizaje.
Consejo. Si esas sugerencias te resultan ruidosas cuando ya domines el flujo, puedes reducirlas con
git config --global advice.statusHints false. Al principio, déjalas.
- Qué significa "No commits yet"
Es una frase que confunde al principiante porque parece decir "no has hecho nada", cuando en realidad tiene un significado técnico muy concreto:
HEAD apunta a la rama main, pero main no apunta a ninguna confirmación.
Es un estado transitorio y perfectamente válido, pero tiene consecuencias prácticas. Muchos comandos necesitan una confirmación de referencia y fallan hasta que exista la primera:
No es un error tuyo: es que HEAD no resuelve a nada. En cuanto haya un commit, todos estos comandos funcionarán con normalidad.
Podemos verlo con la fontanería que aprendimos en el módulo 1:
Este diagrama resume el estado del repositorio de Ana en este momento:
graph LR
HEAD["HEAD"] -->|ref: refs/heads/main| MAIN["main<br/>(no existe todavía)"]
MAIN -.->|apuntará a| C["primer commit<br/>(aún sin crear)"]
OBJ["objects/<br/>vacío"]
WT["Directorio de trabajo<br/>4 ficheros sin seguimiento"]
style MAIN stroke-dasharray: 5 5
style C stroke-dasharray: 5 5
- La primera confirmación del proyecto
Vamos a cerrar el círculo. En Flujo de Trabajo Básico de Git y sobre todo en Preparando y Confirmando Cambios veremos estos comandos con todo detalle; aquí los usamos en su forma más simple para que el repositorio deje de estar vacío.
Paso 1: preparar los cuatro ficheros.
El comando no imprime nada. En Git, el silencio significa éxito. Comprobemos el efecto:
On branch main No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: README.md new file: app.js new file: estilos.css new file: index.html
Los cuatro ficheros han pasado de Untracked files a Changes to be committed: están en el área de preparación. Cada uno aparece marcado como new file porque no existían en ninguna confirmación anterior (no hay ninguna).
Y ahora sí existe el índice:
Y la base de datos ya tiene contenido:
Cuatro objetos: un blob por cada fichero. Todavía no hay ni árbol ni commit; eso lo crea git commit.
Paso 2: confirmar.
[main (root-commit) 1a4c8d6] Estructura inicial del gestor de tareas 4 files changed, 68 insertions(+) create mode 100644 README.md create mode 100644 app.js create mode 100644 estilos.css create mode 100644 index.html
Leamos la primera línea, que es la más informativa:
[main— la rama sobre la que se ha confirmado.(root-commit)— esta marca solo aparece en la primera confirmación de un historial: es un commit sin padre. Recuerda del modelo de datos que un commit normal guarda el hash de su padre; este no tiene ninguno.1a4c8d6— el hash abreviado del nuevo commit.- El mensaje que escribimos.
Las líneas siguientes resumen qué entró: 4 ficheros, 68 líneas añadidas, y el modo 100644 (fichero normal) de cada uno, exactamente la notación que vimos al estudiar los trees.
Paso 3: comprobar el resultado.
Han desaparecido tanto No commits yet como la lista de ficheros. working tree clean significa que el directorio de trabajo coincide exactamente con la última confirmación: no hay nada pendiente. Es el mensaje más tranquilizador de Git.
Y la rama ya existe de verdad:
Un fichero con un hash dentro. Eso es una rama en Git: nada más y nada menos.
HEAD -> main indica que estamos situados en la rama main y que esa rama apunta a este commit. El proyecto de Ana ya tiene historia.
- Convertir un proyecto existente frente a empezar de cero
Los dos escenarios usan el mismo comando, pero las precauciones son distintas.
| Empezar de cero | Convertir proyecto existente | |
|---|---|---|
| Comando típico | git init <nombre> |
cd proyecto && git init |
| Estado inicial | Carpeta vacía | Ficheros ya presentes, sin seguimiento |
| Riesgo principal | Ninguno | Confirmar basura: dependencias, secretos, binarios |
| Paso previo recomendado | Crear un README.md |
Escribir un .gitignore antes del primer git add |
| Primer commit | Casi vacío | Todo el proyecto de golpe |
El riesgo del segundo caso es real y muy frecuente. Imagina que Ana hubiera instalado dependencias antes de inicializar el repositorio:
Si hiciera git add . sin pensar, metería en el historial miles de ficheros de node_modules/ (que se reinstalan con un comando y no aportan nada) y, mucho peor, .env con sus claves. Y como aprendimos en El Modelo de Datos de Git, el historial es inmutable: sacar de ahí un fichero con secretos no es "borrarlo", es reescribir el historial y rotar las claves.
La regla es sencilla: en un proyecto existente, escribe el .gitignore antes del primer git add. Un .gitignore mínimo para el caso de Ana:
Con eso, git status vuelve a mostrar solo lo que interesa. Veremos los patrones, las excepciones y los casos difíciles en Ignorando Archivos con .gitignore; por ahora quédate con la idea de que ese fichero existe y de que su sitio es antes del primer commit.
Un detalle importante: los ficheros existentes no se pierden
Conviene decirlo explícitamente porque genera ansiedad: git init no toca tus ficheros. No los mueve, no los modifica, no los borra. Solo crea .git/. Si ejecutas git init en una carpeta con trabajo de meses, ese trabajo sigue exactamente igual un segundo después. Lo único que cambia es que ahora puedes empezar a versionarlo.
- Repositorios bare: qué son y para qué sirven
Existe una variante que verás mencionada constantemente:
Un repositorio bare (desnudo) es un repositorio sin directorio de trabajo. No contiene index.html ni app.js como ficheros con los que puedas trabajar: contiene únicamente la base de datos de objetos y las referencias, es decir, el contenido de lo que en un repositorio normal sería .git/, pero directamente en la raíz.
¿Te suena? Es exactamente el mismo contenido que vimos dentro de .git/, pero sin carpeta contenedora y sin ficheros del proyecto al lado.
| Repositorio normal | Repositorio bare | |
|---|---|---|
| Directorio de trabajo | Sí | No |
| Puedes editar ficheros | Sí | No |
| Puedes confirmar en él | Sí | No directamente |
| Convención de nombre | gestor-tareas |
gestor-tareas.git |
| Uso típico | Tu copia de trabajo | Servidor central compartido |
Para qué sirve: es la forma correcta de montar el repositorio "central" al que todo el equipo envía y del que todo el equipo descarga. Cuando clonas de GitHub o GitLab, lo que hay al otro lado es un repositorio bare. Se hace así porque, si el repositorio central tuviera directorio de trabajo, recibir cambios podría dejarlo en un estado incoherente respecto a lo que hubiera en sus ficheros.
No lo desarrollamos más aquí: los repositorios bare cobran sentido cuando hablemos de remotos en el módulo 4. Quédate con la definición —repositorio sin copia de trabajo, pensado para compartir— y con el hecho de que en la próxima lección Bruno clonará precisamente de uno.
- Cómo deshacer un
git init accidental
git init accidentalLe pasa a todo el mundo: ejecutas git init, miras el mensaje y descubres una ruta que no esperabas. El caso clásico:
Acabas de convertir toda tu carpeta personal en un repositorio. git status intentará listar decenas de miles de ficheros sin seguimiento y, peor aún, cualquier proyecto que tengas dentro empezará a comportarse de forma rara, porque Git busca el .git más cercano hacia arriba y ahora encuentra ese.
La solución es tranquilizadora por lo simple: borrar la carpeta .git.
En Windows, desde PowerShell:
Eso es todo. Como git init no había hecho nada más que crear esa carpeta, borrarla devuelve el sistema exactamente a como estaba. Tus ficheros no se ven afectados en absoluto.
Precauciones antes de borrar
Aquí conviene ir con cuidado, porque rm -rf .git es inofensivo en un repositorio recién inicializado y catastrófico en uno con historial: borrarías todos los commits, ramas y etiquetas, y no habría forma de recuperarlos (salvo que exista una copia en otro sitio). Antes de ejecutarlo, hazte tres preguntas:
1. ¿Estoy donde creo que estoy?
2. ¿Cuál es el repositorio que voy a borrar?
Este comando devuelve la raíz del repositorio actual, que es justo lo que vas a eliminar. Si la ruta no es la que esperabas, no borres nada todavía.
3. ¿Tiene historial?
Si responde does not have any commits yet, borrar es seguro. Si lista confirmaciones, detente y averigua qué repositorio es antes de tocar nada.
Cómo evitarlo la próxima vez
- Lee siempre la ruta del mensaje de
Initialized.... Un segundo de atención evita el problema entero. - Prefiere
git init <nombre>cuando empieces de cero: crea la carpeta él mismo, así que no puedes equivocarte de sitio. - Comprueba antes de inicializar si ya estás dentro de un repositorio:
Ese fatal es, en este caso, la respuesta que quieres: significa que no estás dentro de ningún repositorio y que git init creará uno nuevo de verdad.
Un aviso sobre repositorios anidados. Si ejecutas
git initdentro de una carpeta que ya está bajo control de versiones, Git no protesta: creas un repositorio dentro de otro. El repositorio interior queda invisible para el exterior y suele acabar en confusión y trabajo perdido. Si de verdad necesitas anidar proyectos, la herramienta correcta son los submódulos, no ungit inita mano.
- Comprobaciones útiles antes de seguir
Un puñado de comandos de diagnóstico que conviene tener a mano:
# ¿Estoy dentro de un repositorio?
git rev-parse --is-inside-work-tree # → true
# ¿Cuál es la raíz del repositorio?
git rev-parse --show-toplevel # → /home/ana/proyectos/gestor-tareas
# ¿Dónde está el directorio .git?
git rev-parse --git-dir # → .git
# ¿Es un repositorio bare?
git rev-parse --is-bare-repository # → false
# ¿En qué rama estoy?
git branch --show-current # → main
# ¿Con qué identidad voy a confirmar aquí?
git config user.name && git config user.emailEsta última es especialmente valiosa cuando usas perfiles condicionales con includeIf, como vimos al final de Configurando Git: te confirma qué identidad se aplicará en este repositorio concreto antes de crear una confirmación con el correo equivocado.
Errores Comunes y Consejos
- Ejecutar
git initsin mirar dónde estás. Es el error número uno. Lee la ruta del mensaje de confirmación; si no es la esperada, borra.gitde inmediato antes de añadir nada. - Creer que
git initya guarda los ficheros. No guarda nada. Un repositorio recién inicializado tiene la base de datos vacía y todos tus ficheros en estado untracked. Hasta el primergit commitno hay ninguna copia de seguridad de nada. - Hacer
git add .como primer comando en un proyecto existente. Sin.gitignore, arrastrarás dependencias, ficheros temporales, binarios de compilación y, en el peor de los casos, secretos. Escribe el.gitignoreprimero y revisagit statusantes de preparar. - Confundir "repositorio vacío" con "carpeta vacía". El mensaje
Initialized empty Git repositoryalarma a mucha gente que teme haber perdido su trabajo. "Vacío" se refiere al historial, no a los ficheros. - Inicializar un repositorio dentro de otro. Git lo permite en silencio y el resultado es desconcertante. Comprueba con
git rev-parse --show-toplevelantes. - No confirmar nada durante días. Un repositorio sin commits no te protege de nada. La primera confirmación, aunque sea imperfecta, ya es una red de seguridad.
- Consejo: acostúmbrate a que el primer commit de un proyecto se llame algo como "Estructura inicial del proyecto". Es la convención más extendida y ayuda a localizar el origen del historial de un vistazo.
- Consejo: si tienes dudas sobre si una carpeta es un repositorio,
ls -aen Linux/macOS odir /aen Windows te lo dice en un segundo: si aparece.git, lo es.
Ejercicios
Ejercicio 1: Crear un repositorio desde cero y observarlo
Crea un repositorio nuevo llamado notas-equipo sin usar mkdir, y responde con comandos —no de memoria— a estas cuatro preguntas:
- ¿A qué rama apunta
HEAD? - ¿Existe ya esa rama como fichero en
refs/heads? - ¿Cuántos objetos hay en la base de datos?
- ¿Existe el fichero
.git/index?
Después crea un README.md con una línea de texto, haz la primera confirmación y vuelve a responder a las cuatro preguntas. Explica qué ha cambiado y por qué.
Ejercicio 2: Convertir un proyecto existente sin meter basura
Prepara esta situación de partida:
mkdir -p ~/practica/tienda-online/node_modules/libreria
cd ~/practica/tienda-online
echo "<h1>Tienda</h1>" > index.html
echo "body { margin: 0; }" > estilos.css
echo "API_KEY=abc123secreto" > .env
echo "ruido" > node_modules/libreria/indice.js
echo "error del jueves" > depuracion.logConvierte la carpeta en repositorio y haz un primer commit que contenga exclusivamente index.html, estilos.css y el .gitignore. Demuestra con la salida de git status que .env, node_modules/ y depuracion.log no aparecen ni siquiera como ficheros sin seguimiento.
Ejercicio 3: Diagnosticar y reparar un git init accidental
Un compañero te escribe: "he ejecutado git init no sé dónde y ahora git status me tarda muchísimo y me lista miles de ficheros que no son de mi proyecto. Además, cuando entro en mi carpeta ~/proyectos/api-clientes y hago git log, me dice que no hay confirmaciones, cuando yo sé que llevo veinte."
- ¿Qué ha pasado, exactamente?
- ¿Qué comandos debe ejecutar para confirmar el diagnóstico antes de tocar nada?
- ¿Cómo lo arregla sin perder el historial de
api-clientes? - ¿Por qué
git logenapi-clientesdecía que no había confirmaciones si el.gitde ese proyecto seguía intacto?
Soluciones
Solución al Ejercicio 1
Estado inicial:
cat .git/HEAD
# → ref: refs/heads/main
ls .git/refs/heads
# → (vacío)
find .git/objects -type f | wc -l
# → 0
ls .git/index
# → ls: cannot access '.git/index': No such file or directoryRespuestas: (1) HEAD apunta a refs/heads/main; (2) no, la rama no existe todavía; (3) cero objetos; (4) no existe el índice.
Después de la primera confirmación:
echo "# Notas del equipo" > README.md
git add README.md
git commit -m "Estructura inicial del proyecto"[main (root-commit) 3f7b2e9] Estructura inicial del proyecto 1 file changed, 1 insertion(+) create mode 100644 README.md
cat .git/HEAD
# → ref: refs/heads/main (sin cambios)
cat .git/refs/heads/main
# → 3f7b2e9c1a5d8b4f2e6a9c3d7b1f5e8a4c2d9b6f (ahora sí existe)
find .git/objects -type f | wc -l
# → 3
ls .git/index
# → .git/index (ahora sí existe)Qué ha cambiado y por qué:
HEADsigue igual. Siempre apuntaba amain; lo que faltaba era la rama, no la referencia.mainya existe como fichero con un hash dentro: el commit le ha dado a la rama algo a lo que apuntar. Aquí se ve literalmente que una rama es un fichero de texto con un hash.- Hay 3 objetos, no 1. Son los tres objetos que exige el modelo de datos de 01-04: un blob con el contenido de
README.md, un tree con el directorio raíz (una entrada: el nombreREADME.mdy el hash del blob) y un commit que apunta a ese tree y no tiene padre. .git/indexexiste porquegit addlo creó al preparar el fichero.
Solución al Ejercicio 2
El orden es lo que resuelve el ejercicio: primero el .gitignore, después git add.
Antes de preparar nada, escribimos las exclusiones:
Comprobación:
.env, node_modules/ y depuracion.log han desaparecido de la lista: Git los ve en el disco pero los ignora, así que ni siquiera los cuenta como untracked. Esa es la demostración que pedía el enunciado.
Ahora ya es seguro preparar todo:
[main (root-commit) 8c2f5a1] Estructura inicial de la tienda 3 files changed, 5 insertions(+) create mode 100644 .gitignore create mode 100644 estilos.css create mode 100644 index.html
Tres ficheros, los tres correctos.
Si alguien hubiera hecho git add . antes de crear el .gitignore, el fichero .env ya estaría en el área de preparación y añadir el .gitignore después no lo sacaría: .gitignore solo afecta a ficheros sin seguimiento. Habría que quitarlo explícitamente del índice, algo que veremos en Preparando y Confirmando Cambios. Y si ya se hubiera confirmado, el secreto quedaría en el historial de forma permanente y habría que rotar la clave.
Solución al Ejercicio 3
1. Qué ha pasado. Ha ejecutado git init en su directorio personal (~) o en ~/proyectos, es decir, en una carpeta por encima de sus proyectos. Ahora existe un .git en ese nivel superior.
Los dos síntomas se explican solos:
git statuslista miles de ficheros porque el repositorio abarca toda la carpeta personal y todo está sin seguimiento.- Dentro de
api-clientesve un historial vacío porque... en realidad no lo ve. Ver el punto 4.
2. Confirmar el diagnóstico sin modificar nada:
cd ~/proyectos/api-clientes
git rev-parse --show-toplevel
# → /home/companero ← la raíz NO es api-clientes: ahí está el problema
git rev-parse --git-dir
# → /home/companero/.git
ls -d ~/.git
# → /home/companero/.git ← existe, no deberíaY comprobar que el repositorio accidental está realmente vacío antes de borrarlo:
Ese fatal es la luz verde: no hay historial que perder.
3. La reparación:
Y verificar que todo ha vuelto a su sitio:
cd ~/proyectos/api-clientes
git rev-parse --show-toplevel
# → /home/companero/proyectos/api-clientes
git log --oneline | head -3
# → sus veinte confirmaciones, intactas4. Por qué git log fallaba. Porque Git busca el directorio .git subiendo por el árbol de directorios desde donde estás, y usa el primero que encuentra. Lo esperable es que el primero sea el de api-clientes... y así era. Aquí está la clave del enunciado: el compañero no estaba dentro de api-clientes cuando lanzó git log, o bien el .git de su proyecto no existía porque nunca lo creó dentro y había estado trabajando, sin saberlo, contra el repositorio de nivel superior.
Este matiz es el que hace peligroso el git init accidental: el .git de arriba no oculta al de abajo, pero sí captura cualquier comando que ejecutes en carpetas que no tengan su propio repositorio. Si el compañero llevaba días haciendo commits desde ~ creyendo que iban a api-clientes, todo su historial estaba en ~/.git y borrarlo con rm -rf lo destruiría. De ahí la insistencia del apartado 9: comprueba siempre git log --oneline antes de borrar. Si hubiera listado confirmaciones, la solución no sería borrar, sino mover ese .git a un lugar seguro y recuperar el trabajo desde él.
Conclusión
El proyecto de Ana ya no es una carpeta: es un repositorio. En esta lección hemos visto que:
git initconvierte el directorio actual en repositorio, ygit init <nombre>crea además la carpeta. Su único efecto es crear.git/; no toca ni guarda tus ficheros.- Lo que crea es el andamiaje vacío que ya conocíamos del modelo de datos:
HEADapuntando amain,objects/sin ningún objeto,refs/heads/sin ninguna rama y ni siquiera un ficheroindex. git statuses la brújula desde el primer segundo: nos dice la rama, que no hay confirmaciones todavía y qué ficheros están sin seguimiento, sugiriendo siempre el comando siguiente.- "No commits yet" tiene un significado técnico:
HEADapunta a una rama que aún no existe. Por esogit logygit diff HEADfallan hasta la primera confirmación. - La primera confirmación es un
root-commit: el único commit del historial sin padre. Al crearla aparecen los blobs, el tree y el commit, yrefs/heads/mainnace como fichero con un hash dentro. - Convertir un proyecto existente exige una precaución extra: escribir el
.gitignoreantes del primergit add, porque el historial es inmutable y sacar de él un secreto es mucho más caro que no meterlo. - Los repositorios bare son repositorios sin copia de trabajo, pensados para servir de punto central compartido.
- Un
git initaccidental se deshace borrando.git, pero solo después de comprobar congit rev-parse --show-toplevelygit log --onelineque ese repositorio no contiene nada valioso.
Ana tiene su repositorio y su primer commit. Pero un proyecto de equipo no vive en un solo portátil: Bruno se incorpora la semana que viene y necesita su propia copia, con todo el historial, en su MacBook. Para eso no sirve git init —eso crearía un repositorio nuevo y vacío, sin relación con el de Ana— sino un comando distinto.
En la siguiente lección, Clonando un Repositorio, veremos qué hace realmente git clone: cómo copia toda la base de datos de objetos, crea el directorio de trabajo, registra el remoto origin y deja a Bruno con un historial idéntico al de Ana. Compararemos los protocolos disponibles (HTTPS, SSH y ruta local), las opciones más útiles como --branch y --depth, y dejaremos clara de una vez la diferencia conceptual entre init y clone.
Dominando Git: De Principiante a Avanzado
Módulo 1: Introducción a Git
- ¿Qué es Git?
- Instalando Git
- Terminología Básica de Git
- El Modelo de Datos de Git
- Configurando Git
- Configuración Inicial
Módulo 2: Operaciones Básicas de Git
- Creando un Repositorio
- Clonando un Repositorio
- Flujo de Trabajo Básico de Git
- Preparando y Confirmando Cambios
- Inspeccionando Cambios con git diff
- Visualizando el Historial de Confirmaciones
Módulo 3: Ramas y Fusión
- Entendiendo las Ramas
- Creando y Cambiando Ramas
- Fusionando Ramas
- Estrategias de Fusión
- Resolviendo Conflictos de Fusión
- Gestión de Ramas
Módulo 4: Trabajando con Repositorios Remotos
- Entendiendo los Repositorios Remotos
- Agregando un Repositorio Remoto
- Autenticación con Repositorios Remotos
- Obteniendo y Extrayendo Cambios
- Enviando Cambios
- Rastreando Ramas
Módulo 5: Operaciones Avanzadas de Git
- Rebase
- Rebase Interactivo
- Cherry-Picking de Confirmaciones
- Guardando Cambios Temporales
- Etiquetando Confirmaciones
- Revirtiendo Confirmaciones
Módulo 6: Herramientas y Técnicas de Git
- Usando Git Hooks
- Git Bisect
- Git Blame
- Git Log y Alias
- Submódulos de Git
- Múltiples Copias de Trabajo con git worktree
Módulo 7: Estrategias de Colaboración y Flujo de Trabajo
- Forks y Pull Requests
- Revisiones de Código con Git
- Flujo de Trabajo Git Flow
- GitHub Flow
- Trunk Based Development
- Integración Continua con Git
Módulo 8: Mejores Prácticas y Consejos de Git
- Escribiendo Buenos Mensajes de Confirmación
- Manteniendo un Historial Limpio
- Ignorando Archivos con .gitignore
- Atributos de Fichero con .gitattributes
- Mejores Prácticas de Seguridad
- Consejos de Rendimiento
Módulo 9: Solución de Problemas y Depuración
- Problemas Comunes de Git
- Deshaciendo Cambios
- Resolviendo Divergencias con el Remoto
- Recuperando Confirmaciones Perdidas
- Tratando con Repositorios Corruptos
- Técnicas Avanzadas de Depuración
