http://alejandroelinformatico.com/slideshows/sneak-git/
Alejandro el informático(@ainformatico). 2012.
Licencia Creative Commons by-nc-sa.
git
?"Git is a free & open source, distributed version control system."
"distributed version control or decentralized version control (DVCS) keeps track of software revisions and allows many developers to work on a given project without necessarily being connected to a common network."
Es un sistema que no está pensado para trabajo colaborativo, pero puede adaptarse a las necesidades de aventureros solitarios.
Usar un sistema de control de versiones pensado exclusivamente para esta tarea.
Todas las comparaciones y comentarios que vienen a continuación son puramente del autor de este documento y están basadas en su experiencia trabajando con las herramientas documentadas. No se pretende intentar cambiar la mentalidad ni la percepción de ninguna persona ni mucho menos desprestigiar alguna de estas herramientas.
"No importa qué herramienta uses, importa que uses una y ésta se adapte a tus necesidades." - Alejandro el informático
git
vs Mercurial vs SVNgit
tracks content, not filesstash
blame
rebase
merge
git
vs hg
git
hg
git
vs svn
git
svn
branch
es en realidad un clon
git
?git
básicoclone
Hay dos maneras de configurar git
:
Los dos métodos nos permiten tener configuraciones locales y generales.
git
, por ejemplo: $ git fix
como alias de $ git checkout -b fix
[user]
name = John Doe
email = [email protected]
[alias]
fix = git checkout -b fix
[diff]
tool = vimdiff
[color "diff"]
old = red
new = blue
$ git config [--global] user.name "John Doe"
$ git config [--global] user.email "[email protected]"
$ git config [--global] alias.fix checkout -b fix
$ git config [--global] color.diff auto
$ git config [--global] core.editor vim
git
GUIsHay dos maneras dependiendo de si queremos tener un repositorio único y personal o compartido con otros desarrolladores(centralizado).
$ git init {name}
$ git init --bare {name}
bare
or not to bare
El bare
indica si se trata de un repositorio el cual permitirá que se hagan commits de otras personas. De esta manera diferentes personas pueden hacer push
/pull
sin romper el working directory de otros desarrolladores.
$ ls
branches config description HEAD hooks info objects refs
.gitignore
Fichero para definir los archivos o directorios que no queremos que se guarden en el repositorio.
$ vim .gitignore
Por ejemplo, añadimos lo siguiente:
tmp/
bin/
*.log
En git
tenemos 3 estados que definen el estado de nuestros ficheros.
$ git status
$ git add {file} [-a]
$ git commit [-m "ADD {file}"]
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# main.c
nothing added to commit but untracked files present (use "git add" to track)
$ git add main.c
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached >file>..." to unstage)
#
# new file: main.c
#
Si queremos añadir solo algunas partes del fichero debemos ejecutar:
git add -p main.c
$ git commit [-m]
ADD main.c for our first commit
# Please enter the commit message for your changes.
# Lines starting with '#' will be ignored,
# and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached >file>..." to unstage)
#
# new file: main.c
#
Podemos ver el histórico de nuestro repositorio con tal de ver qué cambios se han realizado y quién ha sido el autor.
$ git log
commit 1a3024df85560d6b615a7a395c42a99180ed54cb
Author: John Doe <[email protected]>
Date: Sat Oct 15 18:00:00 2011 +0200
ADD main.c for our first commit
El histórico en modo gráfico siempre se ha de leer de abajo hacia arriba.
6.* bcfb7b1 - (HEAD, master) MERGE message description
5.|\
4.| * 9a8c983 - (develop) CHANGE message text
3.* | ca80cc9 - CHANGE just change the message
2.|/
1.* caff1d7 - ADD main.c for our first commit
HEAD
hace referencia a la rama o estado en el que nos encontramos actualmente.
Las ramas (branches) nos permiten hacer un snapshot del repositorio para poder trabajar sin tocar el desarrollo principal, la rama master que es la rama por defecto que se crea.
Por tanto podemos decir que todo en git
son ramas.
En git
una rama no crea una copia entera del proyecto, git
tracks content, not files. Remember?
$ git branch [-d|-D] {name}
$ git branch {name}
$ git branch -d {name}
$ git branch -D {name}
* ca80cc9 - (HEAD, master) CHANGE just change the message
| * 9a8c983 - (develop) CHANGE message text
|/
* caff1d7 - ADD main.c for our first commit
$ git checkout
Nos permite cambiar de rama o restaurar nuestro Working directory o fichero.
$ git checkout -b {name}
$ git checkout {name}
$ git checkout -f [filename]
merge
"Merging (also called integration) in revision control, is a fundamental operation that reconciles multiple changes made to a revision-controlled collection of files."
En la mayoría de casos únicamente hemos de ejecutar:
$ git mergetool
merge
II
* ca80cc9 - (HEAD, master) CHANGE just change the message
| * 9a8c983 - (develop) CHANGE message text
|/
* caff1d7 - ADD main.c for our first commit
$ git merge develop
Auto-merging main.c
CONFLICT (content): Merge conflict in main.
Automatic merge failed; fix conflicts and then commit the result.
mergetool
Usa la herramienta definida o la primera que encuentra que sea capaz de manejar situaciones de merge
$ git mergetool
* bcfb7b1 - (HEAD, master) MERGE message description
|\
| * 9a8c983 - (develop) CHANGE message text
* | ca80cc9 - CHANGE just change the message
|/
* caff1d7 - ADD main.c for our first commit
Para volver atrás en los cambios que hemos realizado lo podemos hacer de diferentes maneras:
Podemos borrar el histórico hasta {n}
commits anteriores, perdiendo todo el que teníamos hasta ahora y dejando el repositorio en aquel estado.
$ git reset HEAD^{n} [--soft|--hard]
* bcfb7b1 - (HEAD, master) MERGE message description
|\
| * 9a8c983 - CHANGE message text
* | ca80cc9 - CHANGE just change the message
|/
* caff1d7 - ADD main.c for our first commit
$ git reset HEAD^1
* ca80cc9 - (HEAD, master) CHANGE just change the message
| * 9a8c983 - (develop) CHANGE message text
|/
* caff1d7 - ADD main.c for our first commit
Podemos crear una rama temporal para poder ver cómo estaba el repositorio en un commit determinado y hacer las acciones pertinentes.
$ git checkout {hash}
* bcfb7b1 - (HEAD, master) MERGE message description
|\
| * 9a8c983 - CHANGE message text
* | ca80cc9 - CHANGE just change the message
|/
* caff1d7 - ADD main.c for our first commit
$ git checkout -b ca80cc9
* bcfb7b1 - (master) MERGE message description
|\
| * 9a8c983 - (develop) CHANGE message text
* | ca80cc9 - (HEAD, temp) CHANGE just change the message
|/
* caff1d7 - ADD main.c for our first commit
Los cambios hechos en commits anteriores se pueden revertir y crear un nuevo commit con el estado de aquel commit.
$ git revert {hash}
$ git revert 196e3f4
* 566874d - (HEAD, master) Revert "ADD f1 file"
* 1fe4b79 - ADD more comments
* 6425178 - ADD comments
* 196e3f4 - ADD f1 file
* bcfb7b1 - MERGE message description
|\
| * 9a8c983 - (develop) CHANGE message text
* | ca80cc9 - CHANGE just change the message
|/
* caff1d7 - ADD main.c for our first commit
clone
Nos permite clonar un repositorio local o remoto para poder empezar a trabajar.
Tenemos diferentes sistemas, dependiendo del protocolo:
ssh
[email protected]:ainformatico/jecookie.git
git
git clone git://github.com/ainformatico/jecookie.git
http[s]
git clone https://github.com/ainformatico/jecookie.git
Los workflows más usuales en git
utilizan las ramas para poder separar el desarrollo en diferentes piezas.
Los workflows se aplican tanto a desarrollos locales como remotos.
Cuando se desarrolla una nueva funcionalidad, se realiza un fix o un test, se piden los cambios de la rama master y si hace falta se hace un merge, se prueban los cambios y se suben a la rama principal.
Normalmente se acostumbra a tener una persona encargada de hacer los merge
a master y controlar el flujo de commits de los desarrolladores.
git
avanzadostash
tag
bundle
patch
blame
submodule
rebase
cherry-pick
stash
stash
Guarda el estado del repositorio modificado en una pila de cambios que se pueden aplicar en cualquier momento.
$ git stash [save {desc}]
$ git stash list
$ git stash show [-p] stash@{n}
$ git stash branch {name} [stash@{n}]
$ git stash apply [stash@{n}]
$ git stash pop [stash@{n}]
$ git stash drop stash@{n}
$ git stash clear
$ git stash [save {desc}]
Guardamos el estado actual del repositorio modificado.
$ git stash save add main declaration
Saved working directory and index state On develop: add main declaration
HEAD is now at 1a3024d ADD main.c four our first commit
* Tenemos los cambios iniciales más los urgentes más los guardados en la pila
|
* Aplicamos los cambios del stash
|\
| |
| |
* | Realizamos los cambios urgentes
| * Guardamos los cambios en el stash
|/
* Cambios actuales
$ git stash list
Listamos el estado de la pila de cambios.
$ git stash list
stash@{0}: On develop: add main declaration
stash@{1}: On fix: fixing bug #16338
stash@{2}: On test: hold for sockets test
* 878b49d - (refs/stash) On master: save add main declaration
|\
| * 10b9166 - index on master: bcfb7b1 MERGE message description
|/
* bcfb7b1 - (HEAD, master) MERGE message description
|\
| * 9a8c983 - (develop) CHANGE message text
* | ca80cc9 - CHANGE just change the message
|/
* caff1d7 - ADD main.c for our first commit
$ git stash show [-p] stash@{n}
Listamos los cambios hechos en el estado n
de la pila.
$ git stash show -p stash@{0}
diff --git a/main.c b/main.c
index e69de29..9d9f7be 100644
--- a/main.c
+++ b/main.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(void)
+{
+ printf("\nHello World.\n");
+ return 0;
+}
$ git stash show stash@{0}
main.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
$ git stash branch {name} [stash@{n}]
Trasladamos los cambios del estado n
a la rama {name}
y borramos el estado de la pila.
$ git stash branch test stash@{0}
Switched to a new branch 'test'
# On branch test
# Changed but not updated:
# (use "git add >file>..." to update what will be committed)
# (use "git checkout -- >file>..." to discard changes in working directory)
#
# modified: main.c
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{0} (4a1fb8c41198a8966a77b506b454745fbaa2587e)
$ git stash apply [stash@{n}]
Aplicamos los cambios de el estado n
.
$ git stash apply
# On branch develop
# Changed but not updated:
# (use "git add >file>..." to update what will be committed)
# (use "git checkout -- >file>..." to discard changes in working directory)
#
# modified: main.c
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git stash pop [stash@{n}]
Igual que $ git stash apply [stash@{n}]
pero elimina el estado de la pila
$ git stash pop stash@{0}
# On branch develop
# Changed but not updated:
# (use "git add >file>..." to update what will be committed)
# (use "git checkout -- >file>..." to discard changes in working directory)
#
# modified: main.c
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{0} (4a1fb8c41198a8966a77b506b454745fbaa2587e)
$ git stash drop stash@{n}
Elimina el estado n
de la pila.
$ git stash drop stash@{0}
Dropped stash@{0} (4a1fb8c41198a8966a77b506b454745fbaa2587e)
$ git stash clear
Elimina toda la pila.
$ git stash clear
tag
Los tags o etiquetas nos permiten marcar e identificar un punto en concreto de todo el histórico de nuestro repositorio.
Normalmente se usan para identificar releases.
$ git tag v0.1 b547e84
$ git tag -a v0.1 -m "The first stable version" b547e84
$ git tag -s v0.1 -m "The first stable version" b547e84
$ git tag [-l {pattern}]
$ git tag -l v0.*
v0.1
v0.2
$ git show v0.1
tag v0.1
Tagger: John Doe <[email protected]>
Date: Sat Oct 15 18:00:00 2011 +0200
The first stable version
commit b547e84694eadd45967c3504c12bb19bd19ce783
Author: John Doe <[email protected]>
Date: Sat Oct 15 18:00:00 2011 +0200
ADD basic main declaration to main file
diff --git a/main.c b/main.c
index e69de29..9d9f7be 100644
--- a/main.c
+++ b/main.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(void)
+{
+ printf("\nHello World.\n");
+ return 0;
+}
bundle
Un bundle
es un paquete que contiene todo o parte de nuestro repositorio y es fácil de transportar y clonar.
$ git bundle create {name} [--all|{tag}|{branch}|{git_date_format}]
$ git pull|clone {name} [{branch}]
patch
Un patch
es un fichero que contiene los cambios de uno o más commits para aplicarlos en un repositorio.
$ git format-patch [{tag}|{hash}] [--stdout > file]
$ git apply --stat {name}
From 577869252ebd0c3c053715fe702e061b8441a6a3 Mon Sep 17 00:00:00 2001
From: John Doe <[email protected]>
Date: Sat, 15 Oct 2011 18:00:00 +0200
Subject: [PATCH] ADD main declaration documentation
---
main.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/main.c b/main.c
index 9d9f7be..6af0170 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,13 @@
#include <stdio.h>
+/**
+ * The main declaration
+ *
+ * @author John Doe <[email protected]>
+ *
+ * @return int
+ *
+ * */
int main(void)
{
printf("\nHello World.\n");
}
blame
blame
nos permite ver por cada línea de código quién ha sido el autor o el último en modificar ésta.
$ git blame main.c
^caff1d7 (Alejandro El Informático 2012-09-27 00:00:00 +0200 1) #include <stdio.h>
^caff1d7 (Alejandro El Informático 2012-09-27 00:00:00 +0200 2)
^caff1d7 (John Doe 2012-09-26 00:00:00 +0200 3) int main(void)
^caff1d7 (John Doe 2012-09-26 00:00:00 +0200 4) {
bcfb7b1c (John Doe 2012-09-25 00:00:00 +0200 5) printf("\n'Hello World' is just an exam...
^caff1d7 (John Doe 2012-09-25 00:00:00 +0200 6) return 0;
^caff1d7 (Alejandro El Informático 2012-09-27 00:00:00 +0200 7) }
Se acabaron echar las culpas a otro.
submodule
Nos permite tener un repositorio git
dentro de un otro repositorio, por ejemplo un proyecto principal para el cual se desarrollan plugins.
Una nota importante sobre submodule
es que estos no se mantienen sincronizados automáticamente, lo hemos de hacer manualmente y para cada cambio que se haga en el submodule
hemos de hacer un commit en nuestro repositorio principal.
submodule
II
Añadir un submodule
.
$ git submodule add ssh://server/path/to/repo {destination}
Initialized empty Git repository in /path/to/repo/{destination}
[...]
$ git st
# On branch develop
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: .gitmodules
# new file: {destination}
#
submodule
III
Cuando hacemos un clon de un repositorio que tiene asociado uno o más submodule
, estos no estarán inicializados por lo tanto hemos de:
$ git submodule init
$ git submodule update
rebase
Nos permite modificar el historial para combinar diferentes commits o reordenarlos.
Por ejemplo:
E---F---G foo/bar
/
A---B---C---D master
Podemos obtener:
A---B---C---D---E---F master
NOTA: no debemos usar rebase
en commits que ya hayan sido publicados.
cherry-pick
Nos permite mover commits a lo largo del historial o entre diferentes ramas.
Un uso común es mover algún commit de una rama a otra para poder continuar el desarrollo.
E---F---G foo/bar
/
A---B---C---D master
Podemos obtener:
E---F---G foo/bar
/
A---B---C---D---F' master
Los hooks son scripts que se ejecutan cuando hay un determinado evento. Estos scripts es encuentran en el directorio .git/hooks
de nuestro repositorio.
podemos agrupar los hooks en:
Son únicamente del cliente, por lo tanto ninguna persona los puede modificar ni se transfieren en un clon, push
o pull
.
pre-commit
prepare-commit-msg
commit-msg
post-commit
applypatch-msg
pre-applypatch
patch
post-applypatch
patch
pre-rebase
rebase
. Evitar rebase
en commits que han estado subidospost-checkout
checkout
. Para inicializar el working directory, compilar, documentación...post-merge
merge
correctamente. Cambiar permisos, copiar ficheros...
Se ejecutan en el servidor antes y después de hacer push
.
pre-receive
push
. Normalmente para controlar permisospost-receive
push
. Notificaciones, parsear el mensaje de commit y cerrar tickets...update
pre-receive
pero controla los ramas para separado
Cuando trabajamos en remoto hacemos push
a un repositorio de tipo bare
cuando ya tenemos una posible versión final de nuestros cambios. Normalmente en local se trabaja en ramas y una vez acabado el trabajo hacemos merge
con nuestra rama master con tal de hacer push
desde ésta hacia a una rama de test o implementación que será revisada y después combinada con la rama master remota.
Tenemos diferentes maneras de enviar los nuestros cambios:
ssh
patch
Pensado para repositorios de solo lectura, y en determinados casos en repositorios de escritura.
git [pull|push] http[s]://servidor/path/to/repo} [{rama}]
Idealmente el git protocol está pensado para tener repositorios read-only, ya que no hay control de usuarios y permisos.
git [pull|push] git://{servidor}/path/to/repo.git [{branch}]
git
ssh
Podemos hacer pull
o push
mediante el protocolo ssh
.
git [pull|push] ssh://{[usuario@]servidor/path/to/repo} [{rama}]
ssh
ssh
en el servidor
Podemos guardar alias para los servidores que usamos, con el fin de facilitar nuestra gestión.
$ git remote add {name} ssh://server/path/to/repo
A partir de este momento podemos hacer:
$ git push {name} {branch}
Al clonar git
automáticamente crea el remote origin, que es aquel destino desde el que nos hemos clonado el repositorio.
git
nos permite crear un visualizador web de nuestro repositorio, normalmente para acceso local o de red local pero también puede servir de acceso público si se configura junto con nuestro servidor web.
Nos permite ver los commits, diff
, log
, branch
, tag
... en nuestro navegador web.
$ git instaweb [--httpd=lighttpd|apache2|webrick] [--stop]
Son un conjunto de scripts que nos permiten tener un control exhaustivo de los usuarios y sus permisos para cada repositorio.
ssh
branch
, tag
y repositorio
Es un hosting de repositorios git
gratuito centrado principalmente con el desarrollo Open Source, pero con características Premium.
Hosting de repositorios mercurial pero desde el 3 de octubre de 2011 proporciona soporte para git
.
hg
a git
, http://bit.ly/33872nsvn
a git
, http://bit.ly/qa7c0B$ git init new-project