-
1. Inicio - Sobre el Control de Versiones
-
2. Fundamentos de Git
-
3. Ramificaciones en Git
-
4. Git en el Servidor
- 4.1 Los Protocolos
- 4.2 Configurando Git en un servidor
- 4.3 Generando tu clave pública SSH
- 4.4 Configurando el servidor
- 4.5 El demonio Git
- 4.6 HTTP Inteligente
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Git en un alojamiento externo
- 4.10 Resumen
-
5. Git en entornos distribuidos
-
6. GitHub
-
7. Herramientas de Git
- 7.1 Revisión por selección
- 7.2 Organización interactiva
- 7.3 Guardado rápido y Limpieza
- 7.4 Firmando tu trabajo
- 7.5 Buscando
- 7.6 Reescribiendo la Historia
- 7.7 Reiniciar Desmitificado
- 7.8 Fusión Avanzada
- 7.9 Rerere
- 7.10 Haciendo debug con Git
- 7.11 Submódulos
- 7.12 Agrupaciones
- 7.13 Replace
- 7.14 Almacenamiento de credenciales
- 7.15 Resumen
-
8. Personalización de Git
-
9. Git y Otros Sistemas
- 9.1 Git como Cliente
- 9.2 Migración a Git
- 9.3 Resumen
-
10. Los entresijos internos de Git
-
A1. Apéndice A: Git en otros entornos
- A1.1 Interfaces gráficas
- A1.2 Git en Visual Studio
- A1.3 Git en Eclipse
- A1.4 Git con Bash
- A1.5 Git en Zsh
- A1.6 Git en Powershell
- A1.7 Resumen
-
A2. Apéndice B: Integrando Git en tus Aplicaciones
- A2.1 Git mediante Línea de Comandos
- A2.2 Libgit2
- A2.3 JGit
-
A3. Apéndice C: Comandos de Git
- A3.1 Configuración
- A3.2 Obtener y Crear Proyectos
- A3.3 Seguimiento Básico
- A3.4 Ramificar y Fusionar
- A3.5 Compartir y Actualizar Proyectos
- A3.6 Inspección y Comparación
- A3.7 Depuración
- A3.8 Parcheo
- A3.9 Correo Electrónico
- A3.10 Sistemas Externos
- A3.11 Administración
- A3.12 Comandos de Fontanería
7.13 Herramientas de Git - Replace
Replace
Los objetos de Git son inmutables, pero proporciona una manera interesante de pretender reemplazar objetos en su base de datos con otros objetos.
El comando replace
te permite especificar un objeto en Git y decirle "cada vez que vea esto, fingir que es esta otra cosa". Esto es más útil para reemplazar un “commit” en tu historial con otro.
Por ejemplo, supongamos que tienes un gran historial de códigos y deseas dividir tu repositorio en un breve historial para nuevos desarrolladores y una historia mucho más larga para las personas interesadas en la minería de datos. Puedes injertar una historia en la otra mediante replace
ingresando el “commit” más antiguo en la nueva línea con el último “commit” en el anterior. Esto es bueno porque significa que en realidad no tienes que reescribir cada “commit” en la nueva historia, como normalmente tendrías que hacer para unirlos juntos (porque el parentesco lo efectúan los SHA-1s).
Vamos a probar esto. Tomemos un repositorio existente, lo dividimos en dos repositorios, uno reciente y otro histórico, y luego veremos cómo podemos recombinarlos sin modificar los repositorios recientes SHA-1 a través de replace
.
Usaremos un repositorio sencillo con cinco compromisos simples:
$ git log --oneline
ef989d8 fifth commit
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Queremos dividir esto en dos líneas de la historia. Una línea pasa de comprometer uno a cometer cuatro - que será el histórico. La segunda línea sólo compromete cuatro y cinco - que será la historia reciente.
Bueno, la creación del historial histórico es fácil, sólo tenemos que poner una rama en la historia y luego empujar esa rama a la rama principal de un nuevo repositorio remoto.
$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Ahora podemos hacer push de la nueva rama history
a la rama` master` de nuestro nuevo repositorio:
$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
* [new branch] history -> master
OK, así que nuestra historia se publica. Ahora la parte más difícil es truncar nuestra historia reciente para hacerla más pequeña. Necesitamos una superposición para que podamos reemplazar un “commit” en una con un “commit” equivalente en la otra, por lo que vamos a truncar esto a sólo cometer cuatro y cinco (y así cometer cuatro superposiciones).
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
En este caso, es útil crear un “commit” de base que tenga instrucciones sobre cómo expandir el historial, por lo que otros desarrolladores saben qué hacer si acceden al primer “commit” en el historial truncado y necesitan más. Por lo tanto, lo que vamos a hacer es crear un objeto de confirmación inicial como nuestro punto base con instrucciones, luego hacemos un “rebase” de los compromisos restantes (cuatro y cinco) encima de él.
Para ello, debemos elegir un punto para dividir, que es 9c68fdc
en SHA-speak (para nosotros es el tercer commit). Por lo tanto, nuestra comisión base se basará en ese árbol. Podemos crear nuestro “commit base” con el comando commit-tree
, que solo toma un árbol y nos dará un nuevo objeto de “commit” sin padres SHA-1.
$ echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Nota
|
El comando |
OK, así que ahora que tenemos un “commit” de base, podemos hacer “rebase” al resto de nuestra historia encima de éste con 'rebase de git --onto`. El argumento --onto
será el SHA-1 que acabamos de regresar de commit-tree
y el punto de “rebase” será el tercer commit (el padre del primer “commit” que queremos mantener, 9c68fdc
):
$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
Así que hemos re-escrito nuestra historia reciente en la parte superior de un tiro de base de comisión que ahora tiene instrucciones sobre cómo reconstruir toda la historia si quisiéramos. Podemos empujar esa nueva historia a un nuevo proyecto y ahora, cuando las personas clonen ese repositorio, solo verán los dos compromisos más recientes y luego un commit de base con instrucciones.
Cambiemos de roles a alguien que clonara el proyecto por primera vez y que quiere toda la historia. Para obtener los datos del historial después de clonar este repositorio truncado, habría que añadir un segundo mando a distancia para el repositorio histórico y buscar:
$ git clone https://github.com/schacon/project
$ cd project
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah
$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
* [new branch] master -> project-history/master
Ahora el colaborador tendría sus compromisos recientes en la rama master
y los compromisos históricos en la rama project-history/master
.
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah
$ git log --oneline project-history/master
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Para combinarlos, simplemente puede llamar a git replace
con el “commit” que desea reemplazar y luego el “commit” con el que desea reemplazarlo. Así que queremos reemplazar el "cuarto" “commit” en la rama maestra con el "cuarto" “commit” en la rama project-history/master
:
$ git replace 81a708d c6e1e95
Ahora bien, si nos fijamos en la historia de la rama master
, parece que se ve así:
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Genial, ¿verdad? Sin tener que cambiar todos los SHA-1s upstream, pudimos reemplazar un “commit” en nuestra historia con un “commit” totalmente diferente y todas las herramientas normales (bisect
,` blame`, etc.) funcionarán como esperamos .
Curiosamente, todavía muestra 81a708d
como el SHA-1, a pesar de que en realidad está utilizando los datos de confirmación c6e1e95
con los que lo reemplazamos. Incluso si ejecuta un comando como cat-file
, le mostrará los datos reemplazados:
$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700
fourth commit
Recuerde que el padre real de 81a708d
fue nuestro “placeholder commit” (622e88e
), no 9c68fdce
, como se indica aquí.
Otra cosa interesante es que estos datos se guardan en nuestras referencias:
$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/replace/81a708dd0e167a3f691541c7a6463343bc457040
Esto significa que es fácil compartir nuestro reemplazo con otros, porque podemos empujar esto a nuestro servidor y otras personas pueden descargarlo fácilmente. Esto no es tan útil en el escenario de injerto de historia que hemos pasado aquí (ya que todo el mundo estaría descargando ambas historias de todos modos, ¿por qué separarlas?). Pero puede ser útil en otras circunstancias.