El universo DVCS es más grande que el de Git. De hecho, hay muchos otros sistemas en este espacio, cada uno con su propio ángulo sobre cómo hacer el control de versión distribuida correctamente. Aparte de Git, el más popular es Mercurial, y los dos son muy similares en muchos aspectos.
La buena noticia, si prefiere el comportamiento del cliente de Git pero está trabajando con un proyecto cuyo código fuente está controlado con Mercurial, es que hay una manera de usar Git como cliente para un repositorio alojado en Mercurial. Dado que la forma en que Git habla con los repositorios del servidor es a través de controles remotos, no debería sorprendernos que este puente se implemente como un ayudante remoto. El nombre del proyecto es 'git-remote-hg', y se puede encontrar en https://github.com/felipec/git-remote-hg.
Primero, necesita instalar git-remote-hg. Esto básicamente implica dejar caer su archivo en algún lugar de su camino, así:
$ curl -o ~/bin/git-remote-hg \
https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg
$ chmod +x ~/bin/git-remote-hg
-
asumiendo que
~ / bin
está en su` $ PATH`. Git-remote-hg tiene otra dependencia: la bibliotecamercurial
para Python. Si tiene instalado Python, es tan sencillo como:
$ pip install mercurial
(Si no tiene instalado Python, visite https://www.python.org/ y consígalo primero.)
Lo último que necesitará es el cliente Mercurial. Vaya a https://www.mercurial-scm.org/ e instálelo si aún no lo ha hecho.
Ahora está listo para el rock. Todo lo que necesita es un repositorio Mercurial al que pueda presionar. Afortunadamente, todos los repositorios de Mercurial pueden actuar de esta manera, así que sólo usaremos el repositorio de "hola mundo" que todos usan para aprender Mercurial:
$ hg clone http://selenic.com/repo/hello /tmp/hello
Ahora que tenemos un repositorio ``server-side'' adecuado, podemos pasar por un flujo de trabajo típico. Como verá, estos dos sistemas son lo suficientemente similares como para que no haya mucha fricción.
Como siempre con Git, primero clonamos:
$ git clone hg::/tmp/hello /tmp/hello-git
$ cd /tmp/hello-git
$ git log --oneline --graph --decorate
* ac7955c (HEAD, origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master, master) Create a makefile
* 65bb417 Create a standard "hello, world" program
Notará que el uso de un repositorio de Mercurial utiliza el comando git clone
estándar.
Esto se debe a que git-remote-hg está funcionando a un nivel bastante bajo, utilizando un mecanismo similar a como se implementa el protocolo HTTP / S de Git (auxiliares remotos).
Dado que Git y Mercurial están diseñados para que cada cliente tenga una copia completa del historial del repositorio, este comando hace un clon completo, incluyendo todo el historial del proyecto, y lo hace con bastante rapidez.
El comando de registro muestra dos confirmaciones, la última de las cuales es señalada por un montón de refs.
Resulta que algunos de estos no están realmente allí.
Echemos un vistazo a lo que realmente está en el directorio .git
:
$ tree .git/refs
.git/refs
├── heads
│ └── master
├── hg
│ └── origin
│ ├── bookmarks
│ │ └── master
│ └── branches
│ └── default
├── notes
│ └── hg
├── remotes
│ └── origin
│ └── HEAD
└── tags
9 directories, 5 files
'Git-remote-hg' está tratando de hacer las cosas más idiomáticamente 'Git-esque', pero bajo el capó es la gestión de la cartografía conceptual entre dos sistemas ligeramente diferentes.
El directorio refs/hg
es donde se almacenan las referencias remotas reales.
Por ejemplo, el refs/hg/origen/branches/default
es un archivo 'ref' de Git que contiene el SHA-1 que comienza con `ac7955c'', que es el 'commit' que señala `master
.
Así que el directorio refs/hg
es como un`refs/remotes/origen' falso, pero tiene la distinción añadida entre marcadores y ramas.
El archivo notes/hg
es el punto de partida de cómo 'git-remote-hg' asigna los hashes de 'commit' de Git a los identificadores de cambios de Mercurial.
Vamos a explorar un poco:
$ cat notes/hg
d4c10386...
$ git cat-file -p d4c10386...
tree 1781c96...
author remote-hg <> 1408066400 -0800
committer remote-hg <> 1408066400 -0800
Notes for master
$ git ls-tree 1781c96...
100644 blob ac9117f... 65bb417...
100644 blob 485e178... ac7955c...
$ git cat-file -p ac9117f
0a04b987be5ae354b710cefeba0e2d9de7ad41a9
Así que refs/notes/hg
apunta a un árbol, que en la base de datos de objetos Git es una lista de otros objetos con nombres.
Git ls-tree
genera el modo, el tipo, el hash de objeto y el nombre de archivo de elementos dentro de un árbol.
Una vez que excavamos hacia abajo a uno de los elementos del árbol, encontramos que en su interior hay un blob llamado ac9117f'' (el hash SHA-1 del 'commit' apuntado por
0a04b98''. Que es el identificador del conjunto de cambios Mercurial en la punta de la rama master
), con contenidos default
).
La buena noticia es, que en general, no tenemos que preocuparnos por todo esto. El flujo de trabajo típico no será muy diferente de trabajar con un control remoto de Git.
Hay una cosa más a la que debemos atender antes de continuar: 'ignore'.
Mercurial y Git usan un mecanismo muy similar para esto, pero es probable que no quiera realmente comprometer un archivo .gitignore
en un repositorio de Mercurial.
Afortunadamente, Git tiene una forma de ignorar los archivos que son locales a un repositorio en disco, y el formato Mercurial es compatible con Git, por lo que sólo tiene que copiarlo:
$ cp .hgignore .git/info/exclude
El archivo .git / info / exclude 'actúa como un
.gitignore`, pero no está incluido en 'commits'.
Supongamos que hemos hecho algunos trabajos e hicimos algunos 'commit' en la rama master
y estamos listos para enviarlo al repositorio remoto.
A continuación, le mostramos nuestro repositorio:
$ git log --oneline --graph --decorate
* ba04a2a (HEAD, master) Update makefile
* d25d16f Goodbye
* ac7955c (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Create a makefile
* 65bb417 Create a standard "hello, world" program
Nuestra rama master
está a dos compromisos por delante de origin/master
, pero estos dos 'commits' sólo existen en nuestra máquina local.
Veamos si alguien más ha estado haciendo un trabajo importante al mismo tiempo:
$ git fetch
From hg::/tmp/hello
ac7955c..df85e87 master -> origin/master
ac7955c..df85e87 branches/default -> origin/branches/default
$ git log --oneline --graph --decorate --all
* 7b07969 (refs/notes/hg) Notes for default
* d4c1038 Notes for master
* df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
| * ba04a2a (HEAD, master) Update makefile
| * d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard "hello, world" program
Puesto que utilizamos el indicador --all
, vemos las `notes refs'' que son utilizadas internamente por 'git-remote-hg', pero podemos ignorarlas.
El resto es lo que esperábamos; `origin / master
ha avanzado por una comisión, y nuestra historia ha divergido ahora.
A diferencia de los otros sistemas con los que trabajamos en este capítulo, Mercurial es capaz de manejar fusiones, por lo que no vamos a hacer nada extravagante.
$ git merge origin/master
Auto-merging hello.c
Merge made by the 'recursive' strategy.
hello.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph --decorate
* 0c64627 (HEAD, master) Merge remote-tracking branch 'origin/master'
|\
| * df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
* | ba04a2a Update makefile
* | d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard "hello, world" program
Perfecto. Hacemos las pruebas y todo pasa, así que estamos listos para compartir nuestro trabajo con el resto del equipo:
$ git push
To hg::/tmp/hello
df85e87..0c64627 master -> master
¡Eso es! Si echa un vistazo al repositorio de Mercurial, verá que esto hizo lo que esperábamos:
$ hg log -G --style compact
o 5[tip]:4,2 dc8fa4f932b8 2014-08-14 19:33 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 64f27bcefc35 2014-08-14 19:27 -0700 ben
| | Update makefile
| |
| o 3:1 4256fc29598f 2014-08-14 19:27 -0700 ben
| | Goodbye
| |
@ | 2 7db0b4848b3c 2014-08-14 19:30 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard "hello, world" program
El conjunto de cambios numerado 2 fue hecho por Mercurial, y los conjuntos de cambios numerados 3 y 4 fueron hechos por 'git-remote-hg', al empujar los 'commit' hechos con Git.
Git tiene sólo un tipo de rama: una referencia que se mueve cuando se hacen los compromisos. En Mercurial, este tipo de referencia se llama marcador, y se comporta de la misma manera que una rama de Git.
El concepto de Mercurial de una "rama" es más pesado.
La rama en la que se realiza un conjunto de cambios se registra con el conjunto de cambios, lo que significa que siempre estará en el historial del repositorio.
He aquí un ejemplo de un 'commit' que se hizo en la rama develop
:
$ hg log -l 1
changeset: 6:8f65e5e02793
branch: develop
tag: tip
user: Ben Straub <[email protected]>
date: Thu Aug 14 20:06:38 2014 -0700
summary: More documentation
Observe la línea que comienza con ``branch''. Git no puede realmente replicar esto (y no necesita, ambos tipos de rama puede representarse como una referencia Git), pero 'git-remote-hg' necesita entender la diferencia, porque Mercurial se preocupa.
Crear marcadores de Mercurial es tan fácil como crear ramas de Git. En el lado Git:
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ git push origin featureA
To hg::/tmp/hello
* [new branch] featureA -> featureA
Eso es todo al respecto. En el lado mercurial, se ve así:
$ hg bookmarks
featureA 5:bd5ac26f11f9
$ hg log --style compact -G
@ 6[tip] 8f65e5e02793 2014-08-14 20:06 -0700 ben
| More documentation
|
o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
|\ Merge remote-tracking branch 'origin/master'
| |
| o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | update makefile
| |
| o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
o | 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard "hello, world" program
Tenga en cuenta la nueva etiqueta [featureA]
en la revisión 5.
Éstos actúan exactamente como las ramas de Git en el lado de Git, con una excepción: usted no puede suprimir un marcador del lado de Git (ésta es una limitación de ayudantes remotos).
Puede trabajar con una rama `heavyweight'' de Mercurial si: introduce ramas en los espacios para `branches
así:
$ git checkout -b branches/permanent
Switched to a new branch 'branches/permanent'
$ vi Makefile
$ git commit -am 'A permanent change'
$ git push origin branches/permanent
To hg::/tmp/hello
* [new branch] branches/permanent -> branches/permanent
Esto es lo que aparece en el lado de Mercurial:
$ hg branches
permanent 7:a4529d07aad4
develop 6:8f65e5e02793
default 5:bd5ac26f11f9 (inactive)
$ hg log -G
o changeset: 7:a4529d07aad4
| branch: permanent
| tag: tip
| parent: 5:bd5ac26f11f9
| user: Ben Straub <[email protected]>
| date: Thu Aug 14 20:21:09 2014 -0700
| summary: A permanent change
|
| @ changeset: 6:8f65e5e02793
|/ branch: develop
| user: Ben Straub <[email protected]>
| date: Thu Aug 14 20:06:38 2014 -0700
| summary: More documentation
|
o changeset: 5:bd5ac26f11f9
|\ bookmark: featureA
| | parent: 4:0434aaa6b91f
| | parent: 2:f098c7f45c4f
| | user: Ben Straub <[email protected]>
| | date: Thu Aug 14 20:02:21 2014 -0700
| | summary: Merge remote-tracking branch 'origin/master'
[...]
El nombre de la rama ``permanent'' se registró en el conjunto de cambios marcados con 7.
Desde el lado de Git, el trabajo con cualquiera de estos estilos de rama es el mismo: sólo checkout'',
commit'', fetch'',
merge'', pull'' y
push'' como lo haría normalmente.
Una cosa que usted debe saber es que Mercurial no apoya la historia de la reescritura, agregando solamente a ella.
Esto es lo que nuestro repositorio de Mercurial parece después de un rebase interactivo'' y un
force-push'':
$ hg log --style compact -G
o 10[tip] 99611176cbc9 2014-08-14 20:21 -0700 ben
| A permanent change
|
o 9 f23e12f939c3 2014-08-14 20:01 -0700 ben
| Add some documentation
|
o 8:1 c16971d33922 2014-08-14 20:00 -0700 ben
| goodbye
|
| o 7:5 a4529d07aad4 2014-08-14 20:21 -0700 ben
| | A permanent change
| |
| | @ 6 8f65e5e02793 2014-08-14 20:06 -0700 ben
| |/ More documentation
| |
| o 5[featureA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
| |\ Merge remote-tracking branch 'origin/master'
| | |
| | o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | | update makefile
| | |
+---o 3:1 318914536c86 2014-08-14 20:00 -0700 ben
| | goodbye
| |
| o 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben
|/ Add some documentation
|
o 1 82e55d328c8c 2005-08-26 01:21 -0700 mpm
| Create a makefile
|
o 0 0a04b987be5a 2005-08-26 01:20 -0700 mpm
Create a standard "hello, world" program
CHangesets 8, 9 y 10 han sido creados y pertenecen a la rama permanent
, pero los viejos ``changesets'' siguen ahí.
Esto puede ser muy confuso para sus compañeros de equipo que están usando Mercurial, así que trate de evitarlo.