Introducción

Preparé este documento como guía para un grupo de personas con las que estaré trabajando. Vamos a desarrollar una aplicación web y decidimos gestionar el trabajo con Git. Algunas personas del grupo son nuevas en git, así que este artículo está pensado como una guía para ellas.

Qué es git

En pocas palabras: Git es una utilidad de control de versiones. Registra los cambios que realizás en los documentos para que puedas revisarlos y volver a cualquier estado anterior en cualquier momento. Es como una máquina del tiempo para tu proyecto.

Git es en realidad mucho más que eso y es muy poderoso, pero por simplicidad vamos a dejarlo así por ahora.

Primeros pasos

Podés empezar con git principalmente de dos maneras.

Estás iniciando el proyecto

Cuando sos vos quien inicia el proyecto, lo primero que tenés que hacer es inicializar el repositorio git.

Creemos la carpeta donde vamos a trabajar.

mkdir ~/mi-proyecto
cd ~/mi-proyecto

Ahora necesitamos iniciar git.

git init

Eso creará una nueva carpeta ~/mi-proyecto/.git que contiene toda la información que git necesita para registrar tus cambios. Nunca tenés que editar ni modificar nada en esa carpeta manualmente, y si la borrás, perderás el historial del proyecto.

Ya tenés el proyecto git iniciado, ahora empezás a trabajar.

Supongamos que creás dos documentos adentro:

emacs main-app.c
emacs sub-rutinas.c

Una vez terminado, tenés que agregar la información a git.

Si revisás el estado del proyecto, vas a ver que hay archivos que git no está siguiendo. Veámoslo:

git status

Vas a obtener esto:

# On branch main
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
main-app.c
sub-rutinas.c
nothing added to commit but untracked files present (use "git add" to track)

Lo que git dice es que hay dos documentos nuevos en la carpeta que está monitoreando, pero ninguno fue agregado explícitamente para seguimiento. Hay que agregarlos.

git add main-app.c
git add sub-rutinas.c

Ya están agregados. Si hay demasiados archivos y querés agregarlos todos de una vez:

git add -A

Revisemos el estado nuevamente:

git status

El resultado es:

# On branch main
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
new file:   main-app.c
new file:   sub-rutinas.c

Los archivos están agregados pero todavía no confirmados. Todavía podés dejar de seguir o desestabilizar un archivo. Si querés hacerlo podés usar git rm [archivo]. En nuestro caso no queremos, así que hagamos nuestro primer commit.

git commit -m 'Mi primer commit. Agrego main-app.c que es el código principal de la app, y llamará algunas subrutinas desde sub-rutinas.c. Este último archivo todavía está vacío'

Ya tenemos los archivos confirmados. Si revisamos el estado una vez más, veremos esto:

git status

El resultado es:

# On branch main
nothing to commit (working directory clean)

El proyecto ya existe en algún lugar, lo estás clonando

Lo que es genial de git es que no hay una autoridad central. Clonás un proyecto y podés trabajar con él en tu PC sin necesidad de estar conectado al repositorio clonado, a menos que quieras compartir tus cambios.

Clonemos un proyecto.

cd ~/
git clone git://servidor.com/mi-proyecto.git

También podés clonar desde tu propio disco o una unidad de red: git clone /compartido/red/proyecto.git

Ahora que tenés el proyecto podés trabajar en él y enviar tus cambios al repositorio "central".

vim README.txt

Agregaste un archivo readme. Revisemos el estado:

git status

Vas a ver esto:

# On branch main
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
README.txt
nothing added to commit but untracked files present (use "git add" to track)

Ahora necesitamos agregar el archivo que acabamos de crear. A partir de acá procedemos igual que antes.

git add README.txt
git commit -m 'agregado archivo README con descripción de la app e instrucciones de instalación'

Eso es todo. Ya empezaste con git.

Resumen

Ahora sabés cómo:

  • Crear un repositorio git
  • Agregar archivos sin seguimiento al proyecto
  • Hacer commits
  • Clonar repositorios y trabajar en ellos

Trabajando con repositorios remotos

En la sección anterior creé un repositorio en ~/mi-proyecto/, y luego lo cloné en ~/segundo-proyecto/, por lo que la segunda carpeta de trabajo es ~/segundo-proyecto/mi-proyecto.

Llamaremos al primero origin, que es como git nombra el repositorio clonado por defecto. Vayamos al primero.

cd ~/mi-proyecto/

Y revisemos los logs:

git log

El resultado es:

commit a6787b87ee36050f58014caa916bf85801771d34
Author: Guillermo Garron - Mac- <ggarron@localhost>
Date:   Mon May 13 15:28:37 2013 -0400

    Mi primer commit. Agrego main-app.c que es el código principal de la app, y llamará algunas subrutinas desde sub-rutinas.c. Este último archivo todavía está vacío

Si ahora vamos a la segunda carpeta:

cd ../segundo-proyecto/mi-proyecto/

Y revisamos el log:

git log

El resultado es:

commit b355a28868f5ac19b6ee949e110d0575dd5ed2dc
Author: Guillermo Garron - Mac- <ggarron@localhost>
Date:   Mon May 13 15:45:07 2013 -0400

    agregado archivo README con descripción de la app e instrucciones de instalación

commit a6787b87ee36050f58014caa916bf85801771d34
Author: Guillermo Garron - Mac- <ggarron@localhost>
Date:   Mon May 13 15:28:37 2013 -0400

    Mi primer commit. Agrego main-app.c que es el código principal de la app, y llamará algunas subrutinas desde sub-rutinas.c. Este último archivo todavía está vacío

Como podés ver, el segundo está un commit adelante. Si queremos compartir los cambios introducidos en el segundo, podemos hacerle push. Pero primero veamos los repositorios bare:

Repositorios bare

Para compartir el trabajo, estos dos desarrolladores virtuales —uno trabajando en ~/mi-proyecto/ y el otro en ~/segundo-proyecto/mi-proyecto/— necesitan un repositorio bare. Un repositorio bare es uno sin árbol de trabajo, compuesto únicamente por la carpeta .git.

En los dos repositorios que tenemos hay una carpeta .git dentro de las carpetas de trabajo. El repositorio bare será solo esa carpeta .git sin carpeta de trabajo. Creemos ese repositorio bare.

mkdir -p ~/tmp/mi-proyecto.git
cd ~/tmp/mi-proyecto.git
git init --bare

Ya tenemos el repositorio bare. Vayamos al segundo proyecto.

cd ~/segundo-proyecto/mi-proyecto/
git remote rm origin
git remote add origin ~/tmp/mi-proyecto.git

Tuvimos que eliminar el origin anterior para agregar uno nuevo, lo tenía porque fue clonado. Básicamente podrías haberlo dejado y simplemente agregar un nuevo remoto, pero prefiero nombrar el repositorio "central" como origin. Revisemos el remoto:

git remote -v

El resultado será algo así:

origin  /Users/ggarron/tmp/mi-proyecto.git (fetch)
origin  /Users/ggarron/tmp/mi-proyecto.git (push)

Ahora podemos hacer push de nuestros cambios:

git push origin main

El resultado:

Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (7/7), 739 bytes, done.
Total 7 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (7/7), done.
To /Users/ggarron/tmp/mi-proyecto.git
 * [new branch]      main -> main

Supongamos que ahora el primer desarrollador virtual quiere obtener nuestros cambios en su carpeta.

cd ~/mi-proyecto/

Necesitamos agregar el origin acá para poder hacer pull de los cambios.

git remote add origin ~/tmp/mi-proyecto.git

No fue necesario eliminar nada acá porque este repositorio no fue clonado, no tiene origin definido.

Hagamos pull de los cambios.

git pull origin main

El resultado es:

 From /Users/ggarron/tmp/mi-proyecto
  * branch            main     -> FETCH_HEAD
  Updating a6787b8..b355a28
  Fast-forward
   README.txt |    2 ++
   1 file changed, 2 insertions(+)
   create mode 100644 README.txt

Como podés ver, el archivo README.txt fue creado con 2 inserciones (era un archivo pequeño). Ahora ambas carpetas están sincronizadas con el repositorio "central".

Resumen

Ahora sabemos cómo:

  • Crear repositorios bare
  • Eliminar y agregar remotos
  • Hacer push de cambios confirmados al repositorio remoto
  • Hacer pull de contenidos desde un repositorio remoto

Trabajando con ramas

Una de las características más poderosas de Git es el trabajo con ramas. Una rama es como un segundo conjunto de carpetas para trabajar, de modo que no arruinás el proyecto real. Es similar a cuando trabajás con un archivo de configuración, digamos http.conf. Querés hacer algunos cambios, pero si algo sale mal querés poder volver rápidamente. Normalmente creás una copia del archivo cp http.conf http.conf.original y ya tenés un respaldo. Git usa ramas para eso.

Por defecto estás trabajando en la rama main, y podés crear tantas ramas como quieras para probar localmente antes de modificar la rama main.

En nuestro ejemplo, supongamos que el primer desarrollador (el jefe) crea una rama llamada testing y la comparte con el otro desarrollador a través del repositorio "central".

cd ~/mi-proyecto/
git checkout -b testing

El resultado es:

Switched to a new branch 'testing'

Git creó la nueva rama y cambió a ella. Probemos una nueva subrutina en nuestra app.

vim sub-rutinas.c

Después de hacer algunos cambios:

git status

El resultado es:

# On branch testing
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
modified:   sub-rutinas.c
#
no changes added to commit (use "git add" and/or "git commit -a")

Agreguemos y confirmemos esos cambios.

git add -A
git commit -m 'cambios en la subrutina principal, solo para pruebas. Se recomienda depurar antes de enviar a producción'

Ahora podemos ver el estado de ambas ramas.

git branch -v

El resultado es:

  main    b355a28 agregado archivo README con descripción de la app e instrucciones de instalación
* testing bf211b2 cambios en la subrutina principal, solo para pruebas. Se recomienda depurar antes de enviar a producción

Como podés ver, estamos trabajando en testing (fijate el asterisco), y también podés ver que testing está adelante. Ahora el jefe quiere compartir su cambio con el otro desarrollador para que lo depure.

git push origin testing

Acá estamos haciendo push de la rama testing a origin (nuestro repositorio central). El resultado que verás es:

Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 429 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /Users/ggarron/tmp/mi-proyecto.git
 * [new branch]      testing -> testing

Ahora el segundo desarrollador hará pull de esos cambios y trabajará con ellos:

cd ~/segundo-proyecto/mi-proyecto/
git checkout -b testing
git pull origin testing

El resultado es:

 From /Users/ggarron/tmp/mi-proyecto
  * branch            testing    -> FETCH_HEAD
 Updating b355a28..bf211b2
 Fast-forward
  sub-rutinas.c |    8 ++++++++
  1 file changed, 8 insertions(+)

Revisemos el estado:

git branch -v

El resultado es:

  main    b355a28 agregado archivo README con descripción de la app e instrucciones de instalación
* testing bf211b2 cambios en la subrutina principal, solo para pruebas. Se recomienda depurar antes de enviar a producción

El segundo desarrollador puede ahora trabajar en los cambios.

emacs sub-rutinas.c
git add sub-rutinas.c
git commit -m 'todo probado y corregido, listo para producción'
git push origin testing

Por su parte, el jefe tomará su copia, hará las pruebas finales y luego fusionará con la rama main (producción).

cd ~/mi-proyecto/
git checkout testing
git pull origin testing

Después de que todas las pruebas estén hechas, es momento de fusionar.

Fusionando ramas en Git

El jefe de desarrollo fusionará ahora la rama testing en la rama main.

Veamos el estado de las ramas:

git branch -v

El resultado muestra:

  main    b355a28 agregado archivo README con descripción de la app e instrucciones de instalación
* testing 0b38a32 todo probado y corregido, listo para producción

Main está muy por detrás de testing. Cambiamos a main:

git checkout main

Y fusionamos ambas:

git merge testing

El resultado es:

Updating b355a28..0b38a32
Fast-forward
 sub-rutinas.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

Notas adicionales

Podés crear tantas ramas como quieras en tu PC local, trabajar y probar con ellas sin necesidad de compartirlas todas.

Para eliminar una rama del origin el comando es:

git push origin --delete testing

testing es solo en nuestro caso; tenés que usar el nombre de la rama que querés eliminar. Considerá que otros podrían estar trabajando en ella antes de borrarla.

Resumen

Ahora sabés cómo:

  • Crear ramas
  • Cambiar entre ramas
  • Hacer push y pull de ramas
  • Fusionar ramas

Conclusión

Git es mucho más que esto, pero estos comandos deberían ser suficientes para proyectos simples y con pocas personas trabajando en ellos.

  • Creado: 13 de mayo de 2013
  • Última edición: 7 de julio de 2026