tag drop delete create crear git version-control mercurial dvcs

drop - Git y Mercurial-Comparación y Contraste



git tag sourcetree (11)

Desde hace un tiempo, he estado usando Subversion para mis proyectos personales.

Cada vez más escucho grandes cosas sobre Git y Mercurial, y DVCS en general.

Me gustaría darle un giro a todo el asunto de DVCS, pero no estoy muy familiarizado con ninguna de las dos opciones.

¿Cuáles son algunas de las diferencias entre Mercurial y Git?

Tenga en cuenta que no estoy tratando de averiguar cuál es el "mejor" o incluso con cuál debería comenzar. Principalmente busco áreas clave donde son similares y donde son diferentes, porque me interesa saber cómo difieren en términos de implementación y filosofía.


Creo que puedes tener una idea de lo que son similares o diferentes a esos sistemas al incluir estos dos videos:

Linus Torvalds en Git ( http://www.youtube.com/watch?v=4XpnKHJAok8 )
Bryan O''Sullivan en Mercurial ( http://www.youtube.com/watch?v=JExtkqzEoHY )

Ambos son muy similares en diseño pero muy diferentes en implementaciones.

Yo uso mercurial. Por lo que yo entiendo de Git, una cosa importante es que rastrea el contenido de los archivos en lugar de los archivos en sí. Linus dice que si mueves una función de un archivo a otro, Git te contará el historial de esa función única a lo largo del movimiento.

También dicen que git es más lento a través de HTTP, pero tiene su propio servidor y protocolo de red.

Git funciona mejor como cliente grueso de SVN que Mercurial. Puede tirar y empujar contra un servidor SVN. Esta funcionalidad aún está en desarrollo en Mercurial.

Tanto Mercurial como Git tienen muy buenas soluciones de alojamiento web disponibles (BitBucket y GitHub), pero Google Code solo admite Mercurial. Por cierto, tienen una comparación muy detallada de Mercurial y Git que hicieron para decidir cuál apoyar ( http://code.google.com/p/support/wiki/DVCSAnalysis ). Tiene mucha buena información.


Escribí una entrada de blog sobre los modelos de ramificación de Mercurial hace un tiempo, e incluí comparaciones con el modelo de ramificación de git. Tal vez lo encuentre interesante: stevelosh.com/blog/entry/2009/8/30/…


Descargo de responsabilidad: uso Git, sigo el desarrollo de Git en la lista de correo de git e incluso contribuyo un poco a Git (principalmente gitweb). Conozco Mercurial por la documentación y parte de la discusión sobre el canal IRC #revctrl en FreeNode.

Gracias a todas las personas en el canal de IRC #mercurial que brindaron ayuda sobre Mercurial para este artículo.

Resumen

Aquí sería bueno tener alguna sintaxis para la tabla, algo como en la extensión PHPMarkdown / MultiMarkdown / Maruku de Markdown

  • Estructura del repositorio: Mercurial no permite la fusión de pulpos (con más de dos padres), ni etiquetar objetos no comprometidos.
  • Etiquetas: Mercurial usa .hgtags archivo versionado .hgtags con reglas especiales para las etiquetas por repositorio, y también admite etiquetas locales en .hg/localtags ; en Git las etiquetas son referencias que residen en refs/tags/ namespace y, de forma predeterminada, son autofollowed en la extracción y requieren empuje explícito.
  • Sucursales: en Mercurial el flujo de trabajo básico se basa en cabezas anónimas ; Git utiliza sucursales con nombre ligero y tiene un tipo especial de sucursales (sucursales de seguimiento remoto ) que siguen a las sucursales en el repositorio remoto.
  • Nombres y rangos de revisión : Mercurial proporciona números de revisión , local al repositorio, y basa revisiones relativas (contando desde la punta, es decir, rama actual) y rangos de revisión en esta numeración local ; Git proporciona una manera de referirse a la revisión relativa a la punta de la rama, y ​​los rangos de revisión son topológicos (según el gráfico de revisiones)
  • Mercurial usa el seguimiento de cambio de nombre , mientras que Git usa la detección de cambio de nombre para tratar el cambio de nombre de archivos
  • Red: Mercurial admite los protocolos "inteligentes" de SSH y HTTP, y el protocolo HTTP estático; Git moderno es compatible con los protocolos "inteligentes" SSH, HTTP y GIT, y el protocolo "tonto" HTTP (S). Ambos tienen soporte para archivos de paquetes para el transporte fuera de línea.
  • Mercurial usa extensiones (plugins) y API establecida; Git tiene capacidad de script y formatos establecidos.

Hay algunas cosas que difieren de Mercurial de Git, pero hay otras cosas que las hacen similares. Ambos proyectos se prestan ideas unos de otros. Por ejemplo, el comando hg bisect en Mercurial (anteriormente extensión bisect ) se inspiró en el comando git bisect en Git, mientras que la idea de git bundle se inspiró en hg bundle .

Estructura del repositorio, almacenando revisiones.

En Git hay cuatro tipos de objetos en su base de datos de objetos: objetos blob que contienen el contenido de un archivo, objetos del árbol jerárquico que almacenan la estructura de directorios, incluidos los nombres de los archivos y las partes relevantes de los permisos de los archivos (permiso ejecutable para archivos, que es un enlace simbólico) , objeto de confirmación que contiene información de autoría, puntero a la instantánea del estado del repositorio en la revisión representada por una confirmación (a través de un objeto de árbol del directorio superior del proyecto) y referencias a cero o más confirmaciones principales, y etiqueta objetos que hacen referencia a otros objetos y pueden Firmar utilizando PGP / GPG.

Git utiliza dos formas de almacenar objetos: formato suelto , donde cada objeto se almacena en un archivo separado (esos archivos se escriben una vez, y nunca se modifican), y formato empaquetado donde muchos objetos se almacenan comprimidos delta en un solo archivo. La atomicidad de las operaciones es proporcionada por el hecho de que la referencia a un nuevo objeto se escribe (atómicamente, usando create + rename trick) después de escribir un objeto.

Los repositorios de Git requieren un mantenimiento periódico con git gc (para reducir el espacio en disco y mejorar el rendimiento), aunque hoy en día Git hace eso automáticamente. (Este método proporciona una mejor compresión de los repositorios.)

Mercurial (por lo que yo entiendo) almacena el historial de un archivo en un registro de archivos (juntos, creo, con metadatos adicionales como el seguimiento de cambio de nombre, y algo de información de ayuda); utiliza una estructura plana llamada manifiesto para almacenar la estructura de directorios y una estructura llamada registro de cambios que almacena información sobre los conjuntos de cambios (revisiones), incluido el mensaje de confirmación y cero, uno o dos padres.

Mercurial utiliza el diario de transacciones para proporcionar una atomicidad de las operaciones, y se basa en el truncado de archivos para la limpieza después de una operación fallida o interrumpida. Los registros son solo de aplicación.

Al observar la estructura del repositorio en Git versus en Mercurial, se puede ver que Git es más como una base de datos de objetos (o un sistema de archivos de contenido), y Mercurial más como una base de datos relacional de campo fijo tradicional.

Diferencias:
En Git los objetos del árbol forman una estructura jerárquica ; En el archivo de manifiesto mercurial es la estructura plana . En el objeto Git blob, almacene una versión de un contenido de un archivo; en Mercurial filelog almacena el historial completo de un solo archivo (si no tomamos en cuenta aquí ninguna complicación con los renombramientos). Esto significa que hay diferentes áreas de operaciones donde Git sería más rápido que Mercurial, todas las demás cosas se consideran iguales (como fusiones o que muestran el historial de un proyecto) y áreas donde Mercurial sería más rápido que Git (como aplicar parches o mostrar historia de un solo archivo). Este problema puede no ser importante para el usuario final.

Debido a la estructura de registro fijo de la estructura de registro de cambios de Mercurial, los compromisos en Mercurial solo pueden tener hasta dos padres ; Las confirmaciones en Git pueden tener más de dos padres (lo que se conoce como "fusión de pulpo"). Si bien puede (en teoría) reemplazar la fusión del pulpo por una serie de fusiones de dos padres, esto podría causar complicaciones al convertir entre los repositorios de Mercurial y Git.

Que yo sepa, Mercurial no tiene equivalentes de etiquetas anotadas (etiquetas de objetos) de Git. Un caso especial de etiquetas anotadas son etiquetas firmadas (con firma PGP / GPG); El equivalente en Mercurial se puede hacer usando GpgExtension , cuya extensión se está distribuyendo junto con Mercurial. No se puede etiquetar el objeto no confirmado en Mercurial como se puede hacer en Git, pero creo que no es muy importante (algunos repositorios de git usan blob etiquetado para distribuir una clave PGP pública para usar para verificar etiquetas firmadas).

Referencias: ramas y etiquetas

En las referencias de Git (sucursales, ramas y etiquetas de seguimiento remoto) residen fuera del DAG de confirmaciones (como deberían). Las referencias en refs/heads/ namespace ( sucursales locales ) apuntan a confirmaciones, y generalmente son actualizadas por "git commit"; apuntan a la punta (cabeza) de la rama, es por eso tal nombre. Las referencias en refs/remotes/<remotename>/ namespace ( ramas de seguimiento remoto ) apuntan a confirmar, siguen a las sucursales en el repositorio remoto <remotename> , y se actualizan con "git fetch" o su equivalente. Las referencias en refs/tags/ namespace ( tags ) generalmente se refieren a confirmaciones (tags ligeros) u objetos de etiquetas (anotados y firmados), y no están destinadas a cambiar.

Etiquetas

En Mercurial puede asignar un nombre persistente a la revisión utilizando la etiqueta ; Las etiquetas se almacenan de forma similar a los patrones de ignorar. Significa que las etiquetas visibles globalmente se almacenan en el archivo .hgtags controlado por .hgtags en su repositorio. Eso tiene dos consecuencias: primero, Mercurial tiene que usar reglas especiales para que este archivo obtenga la lista actual de todas las etiquetas y actualice dicho archivo (p. Ej., Lea la última revisión confirmada del archivo, que no está actualmente revisada); en segundo lugar, debe confirmar los cambios en este archivo para que otros usuarios u otros repositorios puedan ver la nueva etiqueta (según tengo entendido).

Mercurial también admite etiquetas locales , almacenadas en hg/localtags , que no son visibles para otros (y, por supuesto, no son transferibles)

En Git, las etiquetas son referencias fijas (constantes) a otros objetos (generalmente, objetos de etiqueta, que a su vez apuntan a confirmaciones) almacenadas en refs/tags/ namespace. De forma predeterminada, cuando se obtiene o presiona un conjunto de revisiones, git busca o empuja automáticamente las etiquetas que apuntan a las revisiones que se están buscando o empujando. Sin embargo, puede controlar hasta cierto punto qué etiquetas se buscan o se empujan.

Git trata las etiquetas ligeras (que apuntan directamente a las confirmaciones) y las anotadas (que apuntan a las etiquetas), que contienen un mensaje de etiqueta que opcionalmente incluye la firma PGP, que a su vez tiene que comprometerse, de forma ligeramente diferente, por ejemplo, por defecto, solo considera las etiquetas anotadas cuando describe Se compromete usando "git describe".

Git no tiene un equivalente estricto de etiquetas locales en Mercurial. Sin embargo, las mejores prácticas de Git recomiendan configurar un repositorio público separado, en el que se introducen cambios listos, y desde el cual otros clones y captan. Esto significa que las etiquetas (y ramas) que no presiona, son privadas para su repositorio. Por otro lado, también puede usar un espacio de nombres que no sea heads , remotes o tags , por ejemplo local-tags locales para etiquetas locales.

Opinión personal: en mi opinión, las etiquetas deben residir fuera del gráfico de revisión, ya que son externas a él (son indicadores en el gráfico de revisiones). Las etiquetas deben ser no versionadas, pero transferibles. La opción de Mercurial de usar un mecanismo similar al de ignorar archivos significa que debe tratar especialmente los .hgtags (el archivo en el árbol es transferible, pero su .hgtags es ordinaria), o tener etiquetas que son solo locales ( .hg/localtags no está versionado, pero no es .hg/localtags ).

Ramas

En Git, la rama local (punta de rama o cabeza de rama) es una referencia con nombre a un compromiso, donde se pueden hacer nuevos compromisos. La rama también puede significar una línea activa de desarrollo, es decir, todas las confirmaciones accesibles desde la punta de la rama. Las sucursales locales residen en refs/heads/ namespace, por lo que, por ejemplo, el nombre completo de la rama ''master'' es ''refs / heads / master''.

La rama actual en Git (es decir, la rama extraída y la rama a la que irá la nueva confirmación) es la rama a la que hace referencia la referencia HEAD. Uno puede hacer que HEAD apunte directamente a un compromiso, en lugar de ser una referencia simbólica; esta situación de estar en una rama anónima sin nombre se denomina HEAD desconectada ("rama de git" muestra que estás en ''(ninguna rama)'').

En Mercurial hay sucursales anónimas (cabezas de sucursal), y uno puede usar marcadores (a través de la extensión de marcador ). Dichas sucursales de marcadores son puramente locales, y esos nombres no fueron transferibles (hasta la versión 1.6) utilizando Mercurial. Puede usar rsync o scp para copiar el archivo .hg/bookmarks a un repositorio remoto. También puede usar hg id -r <bookmark> <url> para obtener el id de revisión de una sugerencia actual de un marcador.

Desde 1.6 marcadores pueden ser empujados / tirados. La página BookmarksExtension tiene una sección sobre cómo trabajar con repositorios remotos . Hay una diferencia en que en Mercurial los nombres de marcadores son globales , mientras que la definición de ''remoto'' en Git describe también el mapeo de los nombres de las sucursales desde los nombres en el repositorio remoto hasta los nombres de las sucursales locales de seguimiento remoto; por ejemplo refs/heads/*:refs/remotes/origin/* mapeado significa que uno puede encontrar el estado de la rama ''master'' (''refs / heads / master'') en el repositorio remoto en el seguimiento remoto ''origin / master'' rama (''refs / remotes / origin / master'').

Mercurial también se denomina ramas con nombre , donde el nombre de la rama está incrustado en un compromiso (en un conjunto de cambios). Tal nombre es global (transferido en fetch). Esos nombres de las sucursales se registran permanentemente como parte de los metadatos del conjunto de cambios. Con Mercurial moderno puede cerrar "rama con nombre" y dejar de grabar el nombre de la rama. En este mecanismo se calculan puntas de ramas sobre la marcha.

En mi opinión, las "ramas con nombre" de Mercurial deberían llamarse etiquetas de compromiso , porque son lo que son. Hay situaciones en las que la "rama nombrada" puede tener múltiples sugerencias (múltiples confirmaciones sin hijos) y también puede consistir en varias partes desunidas del gráfico de revisiones.

No hay equivalente de esas "ramas incrustadas" de Mercurial en Git; Además, la filosofía de Git es que si bien se puede decir que la rama incluye algún compromiso, no significa que un compromiso pertenezca a alguna rama.

Tenga en cuenta que la documentación de Mercurial aún propone el uso de clones separados (repositorios separados) al menos para las sucursales de larga duración (rama única para el flujo de trabajo del repositorio), también conocida como ramificación por clonación .

Ramas en empuje

Mercurial por defecto empuja todas las cabezas . Si desea empujar una sola rama (una sola cabeza ), debe especificar la revisión de la punta de la rama que desea empujar. Puede especificar la sugerencia de sucursal por su número de revisión (local al repositorio), por el identificador de revisión, por el nombre del marcador (local al repositorio, no se transfiere), o por el nombre de la ramificación incrustada (rama denominada).

Por lo que yo entiendo, si presiona un rango de revisiones que contienen confirmaciones marcadas como en alguna "rama con nombre" en el lenguaje Mercurial, tendrá esta "rama con nombre" en el repositorio al que presiona. Esto significa que los nombres de dichas sucursales incorporadas ("sucursales con nombre") son globales (con respecto a los clones de un repositorio / proyecto dado).

De forma predeterminada (sujeto a la variable de configuración push.default ) "git push" o "git push < remote >" Git empujaría ramas coincidentes , es decir, solo aquellas ramas locales que tienen su equivalente ya presente en el repositorio remoto al que presionas. Puede usar la opción --all para git-push ("git push --all") para empujar todas las ramas , puede usar "git push < remote > < branch >" para empujar una rama individual determinada , y puede usar " git push < remote > HEAD "para empujar la rama actual .

Todo lo anterior supone que Git no está configurado qué ramas se deben enviar a través de remote.<remotename>.push variables de configuración remote.<remotename>.push .

Ramas en fetching

Nota: aquí utilizo la terminología de Git donde "buscar" significa descargar cambios desde el repositorio remoto sin integrar esos cambios con el trabajo local. Esto es lo que hace " git fetch " y " hg pull ".

Si lo comprendo correctamente, Mercurial recupera por defecto todas las cabezas del repositorio remoto, pero puede especificar la rama a buscar a través de " hg pull --rev <rev> <url> " o " hg pull <url>#<rev> " para obtener una sola rama . Puede especificar <rev> usando el identificador de revisión, el nombre de la "rama denominada" (rama incrustada en el registro de cambios) o el nombre del marcador. Sin embargo, el nombre del marcador (al menos actualmente) no se transfiere. Todas las revisiones de "sucursales con nombre" que usted recibe pertenecen a ser transferidas. "hg pull" almacena puntas de ramas que obtuvieron como cabezas anónimas y sin nombre.

En Git de forma predeterminada (para el control remoto ''origin'' creado por "git clone", y para los controles remotos creados con "git remote add") " git fetch " (o " git fetch <remote> ") obtiene todas las sucursales desde el repositorio remoto (desde refs/heads/ namespace), y los almacena en refs/remotes/ namespace. Esto significa, por ejemplo, que la rama denominada ''maestra'' (nombre completo: ''refs / heads / master'') en el ''origen'' remoto se almacenará (guardará) como la rama de seguimiento remoto ''origen / maestra'' (nombre completo: ''refs / mandos a distancia / origen / maestro '').

Puede obtener una sola rama en Git usando git fetch <remote> <branch> - Git almacenaría las ramas solicitadas en FETCH_HEAD, que es algo similar a las cabezas sin nombre Mercurial.

Estos son solo ejemplos de casos predeterminados de la poderosa sintaxis de Git de refspec : con refspecs puede especificar y / o configurar qué ramas desea obtener y dónde almacenarlas. Por ejemplo, el caso predeterminado "fetch todas las ramas" se representa con ''+ refs / heads / *: refs / remotes / origin / *'' refspec de comodín, y "fetch single branch" es una abreviatura de ''refs / heads / <branch>:'' . Los refspecs se utilizan para asignar nombres de ramas (refs) en el repositorio remoto a los nombres de refs locales. Pero no necesita saber (mucho) sobre refspecs para poder trabajar de manera efectiva con Git (gracias principalmente al comando "git remote").

Opinión personal: Personalmente, creo que las "ramas con nombre" (con los nombres de las sucursales incorporadas en los metadatos del conjunto de cambios) en Mercurial tienen un diseño equivocado con su espacio de nombres global, especialmente para un sistema de control de versiones distribuido . Por ejemplo, veamos el caso en el que tanto Alice como Bob tienen una "rama llamada" llamada "for-joe" en sus repositorios, ramas que no tienen nada en común. En el repositorio de Joe, sin embargo, esas dos ramas serían maltratadas como una sola rama. Así que, de alguna manera, ha llegado a una convención que protege contra choques de nombres de sucursales. Esto no es un problema con Git, donde en el repositorio de Joe ''for-joe'' la rama de Alice sería ''alice / for-joe'', y de Bob sería ''bob / for-joe''. Consulte también Separación del nombre de la rama del problema de identidad de rama planteado en la wiki de Mercurial.

Las "sucursales de marcadores" de Mercurial actualmente carecen de un mecanismo de distribución interno.

Diferencias:
Esta área es una de las principales diferencias entre Mercurial y Git, como dijeron James Woodyatt y Steve Losh en sus respuestas. Mercurial, por defecto, utiliza líneas de código anónimas de peso ligero, que en su terminología se denominan "jefes". Git utiliza ramas con nombre ligero, con mapeo inyectivo para asignar nombres de ramas en el repositorio remoto a nombres de ramas de seguimiento remoto. Git lo "obliga" a nombrar sucursales (bueno, con la excepción de una única rama sin nombre, una situación llamada HEAD desapegada), pero creo que esto funciona mejor con flujos de trabajo con muchas ramas como el flujo de trabajo de la rama temática, es decir, múltiples ramas en el paradigma de un solo repositorio.

Revisiones de nombres

En Git hay muchas maneras de nombrar revisiones (descritas, por ejemplo, en la página de manual de git rev-parse ):

  • El nombre completo del objeto SHA1 (cadena hexadecimal de 40 bytes), o una subcadena de tal que sea único dentro del repositorio
  • Un nombre de referencia simbólico, por ejemplo, ''maestro'' (refiriéndose a la rama ''maestra''), o ''v1.5.0'' (refiriéndose a la etiqueta), o ''origen / siguiente'' (refiriéndose a la rama de seguimiento remoto)
  • Un sufijo ^ para el parámetro de revisión significa el primer padre de un objeto de confirmación, ^n significa el noveno padre de una confirmación de combinación. Un sufijo ~n para el parámetro de revisión significa el antecedente n-th de una confirmación en la línea directa del primer padre. Esos sufijos se pueden combinar para formar un especificador de revisión siguiendo la ruta desde una referencia simbólica, por ejemplo, ''pu ~ 3 ^ 2 ~ 3''
  • Salida de "git describe", es decir, una etiqueta más cercana, opcionalmente seguida de un guión y un número de confirmaciones, seguido de un guión, una ''g'' y un nombre de objeto abreviado, por ejemplo ''v1.6.5.1-75- g5bf8097 ''.

También hay especificadores de revisión relacionados con reflog, que no se mencionan aquí. En Git cada objeto, ya sea commit, tag, tree o blob tiene su identificador SHA-1; hay una sintaxis especial como, por ejemplo, ''siguiente: Documentación'' o ''siguiente: README'' para referirse al árbol (directorio) o blob (contenido del archivo) en la revisión especificada.

Mercurial también tiene muchas formas de nombrar conjuntos de cambios (descritos, por ejemplo, en la página de manual de hg ):

  • Un entero llano se trata como un número de revisión. Uno debe recordar que los números de revisión son locales para el repositorio dado ; En otro repositorio pueden ser diferentes.
  • Los enteros negativos se tratan como desviaciones secuenciales de la punta, con -1 que denota la punta, -2 que denota la revisión antes de la punta, y así sucesivamente. También son locales al repositorio.
  • Un identificador de revisión único (cadena hexadecimal de 40 dígitos) o su prefijo único.
  • Un nombre de etiqueta (nombre simbólico asociado con la revisión dada), o un nombre de marcador (con extensión: nombre simbólico asociado con la cabecera dada, local al repositorio), o una "rama con nombre" (etiqueta de confirmación; revisión dada por "rama con nombre") punta (confirmación sin hijos) de todas las confirmaciones con la etiqueta de confirmación dada, con el número de revisión más grande si hay más de una sugerencia de este tipo)
  • El nombre reservado "punta" es una etiqueta especial que siempre identifica la revisión más reciente.
  • El nombre reservado "nulo" indica la revisión nula.
  • El nombre reservado "." Indica el directorio de trabajo padre.

Diferencias
Como puede ver comparando las listas anteriores, Mercurial ofrece números de revisión, locales al repositorio, mientras que Git no lo hace. Por otro lado, Mercurial ofrece compensaciones relativas solo desde ''tip'' (rama actual), que son locales al repositorio (al menos sin la ParentrevspecExtension ), mientras que Git permite especificar cualquier seguimiento de ParentrevspecExtension desde cualquier tip.

La revisión más reciente se llama HEAD en Git y "punta" en Mercurial; No hay revisión nula en Git. Tanto Mercurial como Git pueden tener muchas raíces (pueden tener más de un compromiso sin padres; esto suele ser el resultado de la unión de proyectos anteriormente separados).

Ver también: Muchos tipos diferentes de artículo de especificadores de revisión en Elijah''s Blog (newren''s).

Opinión personal: creo que los números de revisión están sobrevalorados (al menos para el desarrollo distribuido y / o la historia no lineal / ramificada). Primero, para un sistema de control de versiones distribuido, tienen que ser locales al repositorio o requieren que se trate a un repositorio de una manera especial como autoridad central de numeración. En segundo lugar, los proyectos más grandes, con un historial más largo, pueden tener varias revisiones en el rango de 5 dígitos, por lo que ofrecen solo una ligera ventaja sobre los identificadores de revisión de 6-7 caracteres acortados, e implican un ordenamiento estricto, mientras que las revisiones solo están ordenadas parcialmente (me refiero a que las revisiones n y n + 1 no necesitan ser padres e hijos).

Rangos de revisión

En la revisión de Git los rangos son topológicos . A..B sintaxis A..B comúnmente vista, que para el historial lineal significa que el rango de revisión que comienza en A (pero excluyendo A) y que termina en B (es decir, el rango está abierto desde abajo ), es una abreviatura ("azúcar sintáctica") para ^AB , que para el historial, los comandos significan todas las confirmaciones accesibles desde B, excluyendo aquellas accesibles desde A. Esto significa que el comportamiento del rango A..B es completamente predecible (y bastante útil) incluso si A no es ancestro de B: A..B significa luego rango de revisiones del antecesor común de A y B (base de fusión) a la revisión B.

En Mercurial los rangos de revisión se basan en el rango de números de revisión . El rango se especifica utilizando la sintaxis A:B , y al contrario que el rango de Git, actúa como un intervalo cerrado . También el rango B: A es el rango A: B en orden inverso, que no es el caso en Git (pero vea la nota a continuación sobre la sintaxis A...B ). Pero tal simplicidad tiene un precio: el rango de revisión A: B tiene sentido solo si A es antecesor de B o viceversa, es decir, con historia lineal; de lo contrario (supongo que) el rango es impredecible, y el resultado es local al repositorio (porque los números de revisión son locales al repositorio).

Esto se soluciona con Mercurial 1.6, que tiene un nuevo rango de revisión topológica , donde ''A..B'' (o ''A :: B'') se entiende como el conjunto de conjuntos de cambios que son descendientes de X y ancestros de Y. Esto es Supongo que es equivalente a ''--ancestry-path A..B'' en Git.

Git también tiene la notación A...B para la diferencia simétrica de revisiones; significa AB --not $(git merge-base AB) , que significa todas las confirmaciones accesibles desde A o B, pero excluyendo todas las confirmaciones accesibles desde ambas (accesibles desde antecesores comunes).

Renombra

Mercurial utiliza el seguimiento de cambio de nombre para tratar el cambio de nombre de archivos. Esto significa que la información sobre el hecho de que se cambió el nombre de un archivo se guarda en el momento de la confirmación; En Mercurial, esta información se guarda en la forma "diff mejorada" en los metadatos de archivo (registro de archivos). La consecuencia de esto es que tiene que usar hg rename / hg mv ... o debe recordar ejecutar hg addremove para hacer una detección de renombrado basada en la similitud.

Git es único entre los sistemas de control de versiones, ya que utiliza la detección de cambio de nombre para tratar el cambio de nombre de archivos. Esto significa que el hecho de que se cambió el nombre del archivo se detecta en el momento en que se necesita: al realizar una combinación o al mostrar un diff (si se solicita / configura). Esto tiene la ventaja de que se puede mejorar el cambio de nombre del algoritmo de detección, y no se congela en el momento de la confirmación.

Tanto Git como Mercurial requieren el uso de la opción --follow para seguir los --follow cuando se muestra el historial de un solo archivo. Ambos pueden seguir el cambio de nombre cuando se muestra el historial de un archivo en git blame / hg annotate .

En Git, el comando git blame puede seguir el movimiento del código, también mover (o copiar) el código de un archivo a otro, incluso si el movimiento del código no es parte del cambio de nombre del archivo completo. Por lo que sé, esta característica es exclusiva de Git (en el momento de escribir este artículo, octubre de 2009).

Protocolos de red

Tanto Mercurial como Git tienen soporte para obtener y enviar a repositorios en el mismo sistema de archivos, donde la URL del repositorio es solo una ruta del sistema de archivos al repositorio. Ambos también tienen soporte para recuperar archivos de paquetes .

Soporte técnico para obtener y enviar mediante SSH y mediante protocolos HTTP. Para SSH, se necesita una cuenta shell accesible en la máquina de destino y una copia de hg instalada / disponible. Para acceder a HTTP, se requiere la ejecución del script hg-serve o Mercurial CGI, y Mercurial debe estar instalado en la máquina del servidor.

Git admite dos tipos de protocolos utilizados para acceder al repositorio remoto:

  • Los protocolos "inteligentes" , que incluyen el acceso a través de SSH y el protocolo git: // personalizado (por git-daemon ), requieren tener git instalado en el servidor. El intercambio en esos protocolos consiste en que el cliente y el servidor negocien qué objetos tienen en común, y luego generan y envían un paquete de archivos. Git moderno incluye soporte para el protocolo HTTP "inteligente".
  • Los protocolos "tontos" , que incluyen HTTP y FTP (solo para recuperación), y HTTPS (para enviar a través de WebDAV), no requieren que git esté instalado en el servidor, pero sí requieren que el repositorio contenga información adicional generada por git update-server-info (generalmente se ejecuta desde un gancho). El intercambio consiste en que el cliente recorra la cadena de compromiso y descargue objetos sueltos y paquetes de archivos según sea necesario. El inconveniente es que se descarga más de lo estrictamente requerido (por ejemplo, en el caso de la esquina, cuando solo hay un paquete de archivo único, se descargaría completo incluso cuando se recuperan solo unas pocas revisiones), y puede requerir muchas conexiones para finalizar.

Extendiendo: scriptability vs extensiones (plugins)

Mercurial se implementa en Python , con algunos códigos centrales escritos en C para el rendimiento. Proporciona API para escribir extensiones (complementos) como una forma de agregar características adicionales. Algunas de las funciones, como las "sucursales de marcadores" o las revisiones de firmas, se proporcionan en extensiones distribuidas con Mercurial y requieren su activación.

Git se implementa en C , Perl y shell scripts . Git proporciona muchos comandos de bajo nivel ( tuberías ) adecuados para usar en scripts. La forma habitual de introducir una nueva función es escribirla como Perl o script de shell, y cuando la interfaz de usuario se estabilice, vuelva a escribirla en C para mejorar el rendimiento, la portabilidad y, en el caso de shell script, evitar los casos de esquina (este procedimiento se denomina construcción integrada ).

Git se basa y se basa en formatos [repositorio] y protocolos [red]. En lugar de enlaces de idioma, hay reimplementaciones (parciales o completas) de Git en otros idiomas (algunos de ellos son reimplementaciones parciales y envoltorios parciales de los comandos de git): JGit (Java, utilizado por EGit, Eclipse Git Plugin), Grit (Ruby) , Dulwich (Python), git # (C #).

TL; DR


Una diferencia totalmente ajena a los DVCS:

Git parece ser muy popular entre los desarrolladores de C Git es el repositorio de facto para el Kernel de Linux y esta puede ser la razón por la que es tan popular entre los desarrolladores de C. Esto es especialmente cierto para aquellos que tienen el lujo de trabajar solo en el mundo Linux / Unix.

Los desarrolladores de Java parecen favorecer a Mercurial sobre Git. Es posible que haya dos razones para ello: una es que varios proyectos Java muy grandes se alojan en Mercurial, incluido el propio JDK. Otra es que la estructura y la documentación limpia de Mercurial son atractivas para las personas que vienen del campamento de Java, mientras que esas personas encuentran que Git no es coherente con el nombre de comando y carece de documentación. No estoy diciendo que eso sea realmente cierto, estoy diciendo que la gente se ha acostumbrado a algo de su hábitat habitual y luego tienden a elegir DVCS a partir de eso.

Los desarrolladores de Python favorecen casi exclusivamente a Mercurial, supongo. En realidad, no hay ninguna razón racional para eso, excepto el hecho de que Mercurial se basa en Python. (También uso Mercurial y realmente no entiendo por qué las personas se quejan del lenguaje de implementación del DVCS. No entiendo una palabra de Python y si no fuera por el hecho de que está en alguna parte, está basado en Python, entonces no lo habría sabido).

No creo que puedas decir que un DVCS se ajusta a un idioma mejor que otro, por lo que no debes elegir ese. Pero en realidad, las personas eligen (en parte) según a qué DVCS se exponen más como parte de su comunidad.

(No, no tengo estadísticas de uso para respaldar mis afirmaciones anteriores ... todo se basa en mi propia subjetividad)


Después de leer todo sobre que Mercurial es más fácil (lo cual sigo creyendo que lo es, después de que toda la comunidad de Internet es de la opinión), cuando comencé a trabajar con Git y Mercurial sentí que Git es relativamente más simple para mi adaptación a con Mercurial con TortoiseHg) cuando se trabaja desde la línea de comandos, principalmente porque los comandos git fueron nombrados apropiadamente de acuerdo a mí y son menos en número. Mercurial tiene nombres diferentes para cada comando que realiza un trabajo distinto, mientras que los comandos Git pueden ser de uso múltiple según la situación (por ejemplo,checkout). Mientras que Git era más difícil en aquel entonces, ahora la diferencia no es sustancial. YMMV ... Con un buen cliente de GUI como TortoiseHg, es verdad que fue mucho más fácil trabajar con Mercurial y no tuve que recordar los comandos algo confusos. No voy a detallar cómo varía cada comando para la misma acción, pero aquí hay dos listas exhaustivas: 1 del sitio de Mercurial y 2 de wikivs .

╔═════════════════════════════╦════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ Git ║ Mercurial ║ ╠═════════════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════╣ ║ git pull ║ hg pull -u ║ ║ git fetch ║ hg pull ║ ║ git reset --hard ║ hg up -C ║ ║ git revert <commit> ║ hg backout <cset> ║ ║ git add <new_file> ║ hg add <new_file> (Only equivalent when <new_file> is not tracked.) ║ ║ git add <file> ║ Not necessary in Mercurial. ║ ║ git add -i ║ hg record ║ ║ git commit -a ║ hg commit ║ ║ git commit --amend ║ hg commit --amend ║ ║ git blame ║ hg blame or hg annotate ║ ║ git blame -C ║ (closest equivalent): hg grep --all ║ ║ git bisect ║ hg bisect ║ ║ git rebase --interactive ║ hg histedit <base cset> (Requires the HisteditExtension.) ║ ║ git stash ║ hg shelve (Requires the ShelveExtension or the AtticExtension.) ║ ║ git merge ║ hg merge ║ ║ git cherry-pick <commit> ║ hg graft <cset> ║ ║ git rebase <upstream> ║ hg rebase -d <cset> (Requires the RebaseExtension.) ║ ║ git format-patch <commits> ║ hg email -r <csets> (Requires the PatchbombExtension.) ║ ║ and git send-mail ║ ║ ║ git am <mbox> ║ hg mimport -m <mbox> (Requires the MboxExtension and the MqExtension. Imports patches to mq.) ║ ║ git checkout HEAD ║ hg update ║ ║ git log -n ║ hg log --limit n ║ ║ git push ║ hg push ║ ╚═════════════════════════════╩════════════════════════════════════════════════════════════════════════════════════════════════╝

Git guarda un registro de cada versión de los archivos comprometidos internamente, mientras que Hg guarda solo los conjuntos de cambios que pueden tener una huella más pequeña. Git hace que sea más fácil cambiar la historia en comparación con Hg, pero de nuevo es una función de odio o de amor. Me gusta Hg para el primero y Git para el último.

Lo que echo de menos en Hg es la característica de submódulo de Git. Hg tiene subrepos, pero eso no es exactamente un submódulo de Git.

El ecosistema alrededor de los dos también puede influir en la elección de uno: Git tiene que ser más popular (pero eso es trivial), Git tiene GitHub mientras que Mercurial tiene BitBucket , Mercurial tiene TortoiseHg por lo que no he visto un equivalente tan bueno para Git.

Cada uno tiene sus ventajas y desventajas, con cualquiera de ellos no vas a perder.


Echa un vistazo a la publicación de Scott Chacon de hace un tiempo.

Creo que git tiene la reputación de ser "más complicado", aunque en mi experiencia no es más complicado de lo que debería ser. En mi opinión, el modelo git es mucho más fácil de entender (las etiquetas contienen confirmaciones (y los punteros a cero o más confirmaciones principales) contienen árboles que contienen manchas y otros árboles ... hecho).

No es solo mi experiencia que git no es más confuso que mercurial. Recomiendo una vez más leer esta publicación del blog de Scott Chacon al respecto.


Eche un vistazo a Git vs. Mercurial: por favor, relaje la publicación del blog de Patrick Thomson, donde escribe:
Git es MacGyver , Mercurial es James Bond.

Tenga en cuenta que esta publicación del blog es del 7 de agosto de 2008, y ambos SCM han mejorado mucho desde entonces.


He usado Git por un poco más de un año en mi trabajo actual, y antes de eso, usé Mercurial por un poco más de un año en mi trabajo anterior. Voy a proporcionar una evaluación desde la perspectiva de un usuario.

Primero, ambos son sistemas de control de versiones distribuidos. Los sistemas de control de versiones distribuidos requieren un cambio de mentalidad respecto a los sistemas de control de versiones tradicionales, pero en realidad funcionan mucho mejor de muchas maneras una vez que se entienden. Por esta razón, considero que tanto Git como Mercurial son muy superiores a Subversion, Perforce, etc. La diferencia entre los sistemas de control de versiones distribuidas y los sistemas de control de versiones tradicionales es mucho mayor que la diferencia entre Git y Mercurial.

Sin embargo, también hay diferencias significativas entre Git y Mercurial que hacen que cada una se adapte mejor a su propio subconjunto de casos de uso.

Mercurial es más fácil de aprender. Llegué al punto en el que rara vez tenía que consultar documentación o notas después de algunas semanas de usar Mercurial; Todavía tengo que consultar mis notas regularmente con Git, incluso después de usarlas durante un año. Git es considerablemente más complicado.

Esto es en parte porque Mercurial es simplemente más limpio. Rara vez tienes que ramificar manualmente en Mercurial; Mercurial creará una sucursal anónima automáticamente para usted cuando la necesite. La nomenclatura mercurial es más intuitiva; no tiene que preocuparse por la diferencia entre "buscar" y "jalar" como lo hace con Git. Mercurial es un poco menos buggy. Hay problemas de sensibilidad de mayúsculas y minúsculas en los nombres de archivos que solían causar problemas al impulsar proyectos en plataformas con Git y Mercurial; Esto se solucionó en Mercurial hace algún tiempo, mientras que no se habían corregido en Git. Puedes decirle a Mercurial sobre los nombres de archivos; con Git, si no detecta el cambio de nombre automáticamente - una propuesta muy acertada o errada en mi experiencia - no se puede rastrear el cambio de nombre.

La otra razón para la complicación adicional de Git, sin embargo, es que gran parte de ella es necesaria para soportar funciones y potencia adicionales. Sí, es más complicado manejar las ramificaciones en Git, pero por otro lado, una vez que tienes las ramas, no es demasiado difícil hacer cosas con esas ramas que son prácticamente imposibles en Mercurial. Rebasar ramas es una de estas cosas: puedes mover tu rama de modo que su base, en lugar de ser el estado del tronco cuando bifurcas, sea el estado del tronco ahora; esto simplifica en gran medida el historial de versiones cuando hay muchas personas trabajando en la misma base de código, ya que cada uno de los empujes al troncal puede aparecer de forma secuencial, en lugar de entrelazado. Del mismo modo, es mucho más fácil contraer múltiples confirmaciones en su rama en una única confirmación,lo que nuevamente puede ayudar a mantener limpio el historial de control de versiones: idealmente, todo el trabajo en una función puede aparecer como un solo compromiso en el tronco, reemplazando todos los compromisos menores y subramas que el desarrollador pudo haber realizado al desarrollar la función.

En última instancia, creo que la elección entre Mercurial y Git debería depender del tamaño de sus proyectos de control de versiones, medido en términos de la cantidad de personas que trabajan en ellos simultáneamente. Si tiene un grupo de una docena o más trabajando en una sola aplicación web monolítica, por ejemplo, las herramientas de administración de sucursales más potentes de Git lo harán mucho mejor para su proyecto. Por otro lado, si su equipo está desarrollando un sistema distribuido heterogéneo, con solo uno o dos desarrolladores trabajando en cualquier componente a la vez, el uso de un repositorio de Mercurial para cada uno de los proyectos de componentes permitirá que el desarrollo proceda de manera más fluida y con menos Gastos generales de gestión de repositorios.

Conclusión: si tiene un gran equipo que desarrolla una gran aplicación, use Git; Si sus aplicaciones individuales son pequeñas, con cualquier escala proveniente del número en lugar del tamaño de dichas aplicaciones, use Mercurial.


Mercurial está casi completamente escrito en python. El núcleo de Git está escrito en C (y debería ser más rápido que el de Mercurial) y las herramientas escritas en sh, perl, tcl y utiliza utilidades estándar de GNU. Por lo tanto, debe llevar todos estos utils e intérpretes al sistema que no los contiene (por ejemplo, Windows).

Ambos son compatibles con SVN, aunque AFAIK svn es incorrecto para git en Windows (puede ser que solo tenga mala suerte / cojo, quién sabe). También hay extensiones que permiten interoperar entre git y Mercurial.

Mercurial tiene una buena integración de Visual Studio . La última vez que lo verifiqué, el complemento para Git estaba funcionando pero era extremadamente lento.

Los conjuntos de comandos básicos son muy similares (init, clon, add, status, commit, push, pull, etc.). Por lo tanto, el flujo de trabajo básico será el mismo. Además, hay un cliente similar a TortoiseSVN para ambos.

Las extensiones para Mercurial se pueden escribir en python (¡no es una sorpresa!) Y para git se pueden escribir en cualquier forma ejecutable (binario ejecutable, script de shell, etc.). Algunas extensiones son locas poderosas, como git bisect.


Si necesita un buen soporte de Windows, puede preferir Mercurial. TortoiseHg (complemento del explorador de Windows) logra ofrecer una interfaz gráfica fácil de usar a una herramienta bastante compleja. Como estado aquí, también tendrá un complemento de Visual Studio . Sin embargo, la última vez que lo intenté, la interfaz SVN no funcionó tan bien en Windows.

Si no te importa la interfaz de línea de comandos, recomendaría Git. No por razones técnicas, sino por una estratégica. La tasa de adopción de git es mucho mayor. Solo vea cuántos proyectos famosos de código abierto están cambiando de cvs / svn a Mercurial y cuántos están cambiando a Git. Vea cuántos proveedores de alojamiento de código / proyecto puede encontrar con el soporte de git en comparación con el alojamiento de Mercurial.


Yo uso ambos con bastante regularidad. La principal diferencia funcional está en la forma en que Git y Mercurial se ramifican dentro de los repositorios. Con Mercurial, los nombres de las sucursales se clonan y se extraen junto con sus conjuntos de cambios. Cuando agrega cambios a una nueva rama en Mercurial y empuja a otro repositorio, el nombre de la rama se empuja al mismo tiempo. Por lo tanto, los nombres de las ramas son más o menos globales en Mercurial, y usted tiene que usar la extensión Bookmark para tener nombres ligeros solo locales (si los quiere, Mercurial, de forma predeterminada, utiliza líneas de código anónimas ligeras, que en su terminología son llamadas "cabezas"). En Git, los nombres de las sucursales y su asignación inyectiva a las sucursales remotas se almacenan localmente y usted debe gestionarlos explícitamente, lo que significa saber cómo hacerlo.Aquí es prácticamente donde Git obtiene su reputación de ser más difícil de aprender y usar que Mercurial.

Como otros notarán aquí, hay muchas y pequeñas diferencias menores. La cosa con las ramas es el gran diferenciador.