Question Git et Mercurial - Comparer et contraster


Depuis un moment, j'utilise subversion pour mes projets personnels.

De plus en plus, je n'arrête pas d'entendre de grandes choses sur Git et Mercurial, et DVCS en général.

J'aimerais donner un tourbillon à l'ensemble du DVCS, mais je ne connais pas trop l'une ou l'autre option.

Quelles sont certaines des différences entre Mercurial et Git?

Notez que je suis ne pas essayer de savoir lequel est le «meilleur» ou même celui avec lequel je devrais commencer. Je cherche principalement des domaines clés où ils sont similaires et où ils sont différents, parce que je suis intéressé de savoir comment ils diffèrent en termes de mise en œuvre et de philosophie.


512
2017-10-21 04:46


origine


Réponses:


Avertissement:  J'utilise Git, je suis le développement de Git sur la liste de diffusion git, et je contribue même un peu à Git (gitweb principalement). Je connais Mercurial de la documentation et certains de la discussion sur le canal IRC #revctrl sur FreeNode.

Merci à toutes les personnes sur la chaîne IRC #mercurial qui ont fourni de l'aide sur Mercurial pour cette rédaction



Résumé

Ici, ce serait bien d'avoir une syntaxe pour table, quelque chose comme dans PHPMarkdown / MultiMarkdown / Maruku extension de Markdown

  • Structure du référentiel: Mercurial ne permet pas la fusion de poulpes (avec plus de deux parents), ni le marquage d'objets non-commit.
  • Mots clés: Mercurial utilise versionné .hgtags fichier avec des règles spéciales pour les balises per-repository, et prend également en charge les balises locales dans .hg/localtags; dans les tags Git sont des références résidant dans refs/tags/ namespace, et par défaut sont autofollowed sur la récupération et nécessitent un push explicite.
  • Branches: Dans Mercurial, le workflow de base est basé sur têtes anonymes; Git utilise des branches nommées légères, et a un genre particulier de branches (branches de suivi à distance) qui suivent les branches dans le référentiel distant.
  • Révision des noms et des gammes: Mercurial fournit numéros de révision, local au référentiel, et base les révisions relatives (en comptant à partir de la pointe, c'est-à-dire la branche actuelle) et les plages de révision sur ce local numérotage; Git fournit un moyen de se référer à la révision par rapport à l'extrémité de la branche, et les intervalles de révision sont topologiques (d'après le graphique des révisions)
  • Utilisations mercurielles renommer le suivi, alors que Git utilise renommer la détection gérer les renames de fichiers
  • Réseau: Mercurial prend en charge les protocoles «intelligents» SSH et HTTP et le protocole HTTP statique; Git moderne prend en charge les protocoles «intelligents» SSH, HTTP et GIT, et le protocole «bête» HTTP (S). Les deux prennent en charge les fichiers groupés pour le transport hors ligne.
  • Utilisations mercurielles extensions (plugins) et API établie; Git a scriptabilité et formats établis.

Il y a quelques choses qui diffèrent de Gur par Mercurial, mais il y a d'autres choses qui les rendent similaires. Les deux projets empruntent des idées les uns aux autres. Par exemple hg bisect commande dans Mercurial (anciennement bisect extension) a été inspiré par git bisect commande en Git, alors que l'idée de git bundle a été inspiré par hg bundle.

Structure du référentiel, stockage des révisions

Dans Git, il existe quatre types d'objets dans sa base de données d'objets: goutte objets qui contiennent le contenu d'un fichier, hiérarchique arbre les objets qui stockent la structure de répertoires, y compris les noms de fichiers et les parties pertinentes des permissions de fichiers (autorisation exécutable pour les fichiers, étant un lien symbolique), commettre objet qui contient des informations d'auteur, pointeur vers l'instantané de l'état du dépôt à la révision représenté par une validation (via un objet arborescence du répertoire principal du projet) et des références à zéro ou plusieurs commits parents, et marque objets qui référencent d'autres objets et peuvent être signés en utilisant PGP / GPG.

Git utilise deux manières de stocker des objets: en vrac format, où chaque objet est stocké dans un fichier séparé (ces fichiers sont écrits une fois, et jamais modifié), et emballé format où de nombreux objets sont stockés sous forme delta dans un seul fichier. L'atomicité des opérations est fournie par le fait que la référence à un nouvel objet est écrite (de manière atomique, en utilisant create + renommer trick) après avoir écrit un objet.

Les dépôts Git nécessitent une maintenance périodique en utilisant git gc (pour réduire l'espace disque et améliorer les performances), bien que de nos jours, Git le fasse automatiquement. (Cette méthode fournit une meilleure compression des référentiels.)

Mercurial (autant que je le comprends) stocke l'histoire d'un fichier dans un filelog (ensemble, je pense, avec des métadonnées supplémentaires comme le suivi du changement de nom et quelques informations d'aide); il utilise une structure plate appelée manifeste pour stocker la structure du répertoire, et la structure appelée changelog qui stockent des informations sur les changesets (révisions), y compris le message de validation et zéro, un ou deux parents.

Utilisations mercurielles journal des transactions pour fournir l'atomicité des opérations, et repose sur tronquer fichiers à nettoyer après une opération échouée ou interrompue. Les Revlogs sont uniquement en annexe.

En regardant la structure du dépôt dans Git par rapport à Mercurial, on peut voir que Git ressemble davantage à une base de données d'objets (ou à un système de fichiers adressé par le contenu), et Mercurial plus à une base de données relationnelle traditionnelle.

Différences:
Dans Git le arbre les objets forment un hiérarchique structure; à Mercurial manifeste le fichier est appartement structure. Dans Git goutte magasin d'objets une version d'un contenu d'un fichier; à Mercurial filelog magasins histoire entière d'un seul fichier (si nous ne prenons pas en compte ici les complications avec les renames). Cela signifie qu'il existe différentes zones d'opérations où Git serait plus rapide que Mercurial, toutes autres choses considérées comme égales (comme les fusions, ou montrant l'historique d'un projet), et les zones où Mercurial serait plus rapide que Git (comme appliquer des correctifs, ou montrer histoire d'un seul fichier). Ce problème peut ne pas être important pour l'utilisateur final. 

En raison de la structure d'enregistrement fixe de Mercurial changelog structure, commet dans Mercurial peut avoir seulement jusqu'à deux parents; Commit in Git peut avoir plus de deux parents (soi-disant "fusion de pieuvres"). Bien que vous puissiez (en théorie) remplacer la fusion de pieuvres par une série de fusions à deux parents, cela peut entraîner des complications lors de la conversion entre les référentiels Mercurial et Git.

Autant que je sache, Mercurial n'a pas d'équivalent de balises annotées (objets d'étiquette) de Git. Un cas particulier d'étiquettes annotées sont balises signées (avec signature PGP / GPG) équivalent en Mercurial peut être fait en utilisant GpgExtension, quelle extension est distribuée avec Mercurial. Tu ne peux pas tag objet non-commit dans Mercurial comme vous pouvez le faire dans Git, mais ce n'est pas très important, je pense (certains dépôts git utilisent des blob marqués pour distribuer la clé publique PGP à utiliser pour vérifier les balises signées).

Références: branches et tags

Dans Git, les références (branches, branches et balises de suivi à distance) résident en dehors du DAG des validations (comme elles le devraient). Références dans refs/heads/ espace de noms (branches locales) point à commits, et sont généralement mis à jour par "commit git"; ils pointent vers la pointe de la branche, c'est pourquoi un tel nom. Références dans refs/remotes/<remotename>/ espace de noms (branches de suivi à distance) point à valider, suivez les branches dans le référentiel distant <remotename>, et sont mis à jour par "git fetch" ou équivalent. Références dans refs/tags/ espace de noms (Mots clés) pointe généralement vers des commits (balises légères) ou des objets de balises (balises annotées et signées) et ne sont pas destinés à changer.

Mots clés

Dans Mercurial vous pouvez donner un nom persistant à la révision en utilisant marque; les balises sont stockées de manière similaire aux motifs ignorés. Cela signifie que les balises visibles globalement sont stockées dans des révisions contrôlées .hgtags fichier dans votre référentiel. Cela a deux conséquences: premièrement, Mercurial doit utiliser des règles spéciales pour ce fichier pour obtenir la liste actuelle de toutes les balises et pour mettre à jour ce fichier (par exemple, il lit la dernière révision validée du fichier, pas la version extraite); Deuxièmement, vous devez valider les modifications apportées à ce fichier pour que les nouveaux utilisateurs / autres référentiels puissent voir le nouveau tag (pour autant que je le comprenne).

Mercurial soutient également tags locaux, stocké dans hg/localtags, qui ne sont pas visibles par les autres (et bien sûr ne sont pas transférables)

Les balises In Git sont des références nommées fixes (constantes) à d’autres objets (généralement des objets de balise, qui à leur tour pointent vers des commits) stockées dans refs/tags/ espace de nommage. Par défaut, lors de l'extraction ou de l'envoi d'un ensemble de révisions, git récupère ou envoie automatiquement les balises indiquant que des révisions sont extraites ou poussées. Néanmoins, vous pouvez contrôle dans une certaine mesure quelles balises sont récupérées ou poussé.

Git traite les balises légères (pointant directement vers les commits) et les balises annotées (pointant vers les objets de balise, qui contiennent un message de balise qui inclut éventuellement la signature PGP, qui à son tour doit être validée). commet en utilisant "git describe".

Git n'a pas d'équivalent strict des tags locaux dans Mercurial. Néanmoins, les meilleures pratiques git recommandent d'installer un référentiel nu public séparé, dans lequel vous poussez les modifications prêtes, et à partir duquel les autres clonent et récupèrent. Cela signifie que les balises (et les branches) que vous n'appuyez pas sont privées dans votre référentiel. D'un autre côté, vous pouvez également utiliser un espace de noms autre que heads, remotes ou tags, par exemple local-tags pour les tags locaux.

Opinion personnelle: À mon avis, les balises devraient résider à l'extérieur du graphe de révision, car elles sont externes (elles sont des pointeurs dans le graphique des révisions). Les tags doivent être non-versionnés, mais transférables. Le choix de Mercurial d'utiliser un mécanisme similaire à celui pour ignorer les fichiers, signifie que soit il doit traiter .hgtags spécialement (fichier dans l'arbre est transférable, mais ordinaire il est versionné), ou avoir des tags qui sont locaux seulement (.hg/localtags est non-versionné, mais non transférable).

Branches

Dans Git branche locale (astuce de branche, ou tête de branche) est une référence nommée à un commit, où l'on peut développer de nouveaux commits. Une branche peut également signifier une ligne active de développement, c'est-à-dire que tous les commits peuvent être atteints à partir de la pointe de la branche. Les sections locales résident dans refs/heads/ espace de noms, par exemple nom complet de la branche 'master' est 'refs / heads / master'.

La branche courante dans Git (signifiant branchée branche, et branche où la nouvelle validation ira) est la branche référencée par la réf. HEAD. On peut avoir HEAD pointant directement sur un commit, plutôt que d'être une référence symbolique; cette situation d'être sur une branche anonyme sans nom est appelée TETE détachée ("branche git" montre que vous êtes sur '(pas de branche)').

Dans Mercurial il y a des branches anonymes (têtes de branche), et on peut utiliser des signets (via extension de signet). Tel branches de signets sont purement locaux, et ces noms étaient (jusqu'à la version 1.6) non transférables en utilisant Mercurial. Vous pouvez utiliser rsync ou scp pour copier le .hg/bookmarks fichier vers un référentiel distant. Vous pouvez aussi utiliser hg id -r <bookmark> <url> pour obtenir l'identifiant de révision d'une pointe actuelle d'un signet.

Depuis 1.6 les signets peuvent être poussés / tirés. le FavorisExtension la page a une section sur Travailler avec des référentiels distants. Il y a une différence en ce que dans les noms de signets Mercurial sont global, tandis que la définition de 'remote' dans Git décrit aussi mappage des noms de branche des noms dans le référentiel distant aux noms des branches locales de suivi à distance; par exemple refs/heads/*:refs/remotes/origin/* le mapping signifie que l'on peut trouver l'état de la branche 'master' ('refs / heads / master') dans le référentiel distant dans la branche de suivi à distance 'origin / master' ('refs / remotes / origin / master').

Mercurial a aussi appelé branches nommées, où le nom de la branche est intégré dans un commit (dans un changeset). Ce nom est global (transféré sur fetch). Ces noms de branches sont enregistrés en permanence dans les métadonnées de changeset. Avec Mercurial moderne, vous pouvez fermer la "branche nommée" et arrêter l'enregistrement du nom de la branche. Dans ce mécanisme, les extrémités des branches sont calculées à la volée.

Selon moi, les "branches nommées" de Mercurial devraient être appelées étiquettes de validation à la place, parce que c'est ce qu'ils sont. Il y a des situations où la "branche nommée" peut avoir plusieurs astuces (plusieurs commits sans enfant), et peut aussi consister en plusieurs parties disjointes du graphique des révisions.

Il n'y a aucun équivalent de ces "branches incorporées" de Mercurial dans Git; de plus, la philosophie de Git est que, bien que l’on puisse dire que cette branche inclut des commit, cela ne signifie pas qu’un commit appartient à une branche.

Notez que la documentation de Mercurial propose toujours d'utiliser des clones séparés (référentiels distincts) au moins pour les branches à vie longue (branche unique par workflow de référentiel), branchement par clonage.

Branches en poussant

Mercurial par défaut pousse toutes les têtes. Si vous voulez pousser une seule branche (tête unique), vous devez spécifier la révision de pointe de la branche que vous voulez pousser. Vous pouvez spécifier l'astuce de la branche par son numéro de révision (local vers le référentiel), par l'identificateur de révision, par le nom du signet (local vers le référentiel, n'est pas transféré) ou par le nom de la branche incorporée (branche nommée).

Si je comprends bien, si vous mettez une série de révisions qui contiennent des commits marqués comme étant sur une "branche nommée" dans le jargon mercuriel, vous aurez cette "branche nommée" dans le dépôt vers lequel vous poussez. Cela signifie que les noms de ces branches incorporées ("branches nommées") sont global (en ce qui concerne les clones du référentiel / projet donné).

Par défaut (sous réserve de push.default variable de configuration) "git push" ou "git push <éloigné> "Git pousserait branches correspondantes, c'est-à-dire uniquement les branches locales dont l'équivalent est déjà présent dans le référentiel distant dans lequel vous insérez. Vous pouvez utiliser --all option de git-push ("git push --all") pour pousser toutes les branches, vous pouvez utiliser "git push <éloigné> <branche> "pour pousser un branche unique donnée, et vous pouvez utiliser "git push <éloigné> HEAD "pour pousser branche actuelle.

Tout ce qui précède suppose que Git n'est pas configuré quelles branches à pousser via remote.<remotename>.push  variables de configuration.

Branches à la recherche

Remarque: ici j'utilise la terminologie Git où "chercher" signifie télécharger des changements depuis le dépôt distant sans pour autant intégrer ces changements au travail local. C'est quoi "git fetch" et "hg pull" Est-ce que.

Si je comprends bien, par défaut Mercurial récupère toutes les têtes à partir du référentiel distant, mais vous pouvez spécifier la branche à récupérer via "hg pull --rev <rev> <url>" ou "hg pull <url>#<rev>" obtenir branche unique. Vous pouvez spécifier <rev> en utilisant l'identificateur de révision, le nom "branche nommée" (branche intégrée au journal des modifications) ou le nom du signet. Cependant, le nom du signet (au moins actuellement) n'est pas transféré. Toutes les révisions de «branches nommées» que vous obtenez sont transférées. "hg pull" stocke les astuces des branches trouvées comme des têtes anonymes et sans nom.

En Git par défaut (pour 'origine' remote créé par "git clone", et pour les télécommandes créées en utilisant "git remote add") "git fetch" (ou "git fetch <remote>") obtient toutes les branches à partir du référentiel distant (à partir de refs/heads/ espace de noms), et les stocke dans refs/remotes/ espace de nommage. Cela signifie par exemple que la branche nommée 'master' (nom complet: 'refs / heads / master') en 'origine' distante serait stockée (enregistrée) comme 'origine / maître' branche de suivi à distance (nom complet: 'refs / remotes / origin / master').

Vous pouvez aller chercher branche unique en Git en utilisant git fetch <remote> <branch> - Git stockerait la ou les branches demandées dans FETCH_HEAD, qui est quelque chose de similaire aux têtes sans nom de Mercurial.

Ce ne sont que des exemples de cas par défaut de puissants refspec Syntaxe Git: avec refspecs, vous pouvez spécifier et / ou configurer les branches que vous souhaitez récupérer et où les stocker. Par exemple, le cas par défaut "aller chercher toutes les branches" est représenté par '+ refs / heads / *: refs / remotes / origin / *' wildcard refspec, et "fetch single branch" est un raccourci pour 'refs / heads / <branche>:' . Refspecs sont utilisés pour mapper les noms des branches (refs) dans le référentiel distant vers les noms des refs locaux. Mais vous n'avez pas besoin de savoir (beaucoup) sur refspecs pour pouvoir travailler efficacement avec Git (merci principalement à la commande "git remote").

Opinion personnelle: Personnellement, je pense que "branches nommées" (avec des noms de branche incorporés dans les métadonnées changeset) dans Mercurial sont une conception erronée avec son espace de noms global, en particulier pour un distribué système de contrôle de version. Par exemple, prenons le cas où Alice et Bob ont "une branche nommée" nommée "for-joe" dans leurs dépôts, des branches qui n'ont rien en commun. Dans le dépôt de Joe, cependant, ces deux branches seraient maltraitées en une seule branche. Vous avez donc créé une convention protégeant contre les conflits de nom de branche. Ce n'est pas un problème avec Git, où dans le dépôt de Joe la branche 'for-joe' de Alice serait 'alice / for-joe', et de Bob ce serait 'bob / for-joe'. Voir également Séparer le nom de la succursale de l'identité de la succursale question soulevée sur le wiki Mercurial.

Les «branches de signets» de Mercurial manquent actuellement de mécanisme de distribution interne.


443
2017-10-21 10:23



Je pense que vous pouvez avoir une idée de ce que ces systèmes sont similaires ou différents en réalisant ces deux vidéos:

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

Les deux sont très similaires dans la conception, mais très différentes dans les implémentations.

J'utilise Mercurial. Pour autant que je comprenne Git, une chose importante git est différent, c'est qu'il suit le contenu des fichiers au lieu des fichiers eux-mêmes. Linus dit que si vous déplacez une fonction d'un fichier à un autre, Git vous indiquera l'historique de cette fonction unique dans le mouvement.

Ils disent aussi que git est plus lent sur HTTP mais il a son propre protocole réseau et son propre serveur.

Git fonctionne mieux comme un client SVN épais que Mercurial. Vous pouvez tirer et pousser contre un serveur SVN. Cette fonctionnalité est encore en développement chez Mercurial

Mercurial et Git ont tous les deux de très jolies solutions d'hébergement Web (BitBucket et GitHub), mais Google Code ne prend en charge que Mercurial. En passant, ils ont une comparaison très détaillée de Mercurial et Git qu'ils ont fait pour décider lequel soutenir (http://code.google.com/p/support/wiki/DVCSAnalysis). Il a beaucoup de bonnes informations.


56
2017-10-21 05:04



J'ai écrit un article sur les modèles de branchement de Mercurial il y a un certain temps et j'ai inclus des comparaisons avec le modèle de branchement de Git. Peut-être que vous le trouverez intéressant: http://stevelosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/


29
2017-10-21 13:27



J'utilise les deux assez régulièrement. La principale différence fonctionnelle est la façon dont Git et Mercurial nomment les branches dans les dépôts. Avec Mercurial, les noms de branches sont clonés et extraits avec leurs changesets. Lorsque vous ajoutez des modifications à une nouvelle branche dans Mercurial et que vous poussez vers un autre référentiel, le nom de la branche est poussé en même temps. Ainsi, les noms de branches sont plus ou moins globaux dans Mercurial, et vous devez utiliser l'extension Bookmark pour avoir des noms légers locaux (si vous le souhaitez), Mercurial utilise par défaut des codages légers, qui sont dans sa terminologie appelé "têtes"). Dans Git, les noms de branche et leur mappage injectif aux branches distantes sont stockés localement et vous devez les gérer explicitement, ce qui signifie savoir comment procéder. C'est à peu près là où Git obtient sa réputation d'être plus difficile à apprendre et à utiliser que Mercurial.

Comme d'autres le noteront ici, il y a beaucoup de différences mineures. La chose avec les branches est le grand différenciateur.


25
2017-10-21 05:26



Jeter un coup d'œil à Git vs Mercurial: S'il vous plaît se détendre article de blog de Patrick Thomson, où il écrit:
Git est MacGyver, Mercurial est James Bond

Notez que cet article de blog date du 7 août 2008, et que les deux SCM se sont beaucoup améliorés depuis.


18
2017-10-22 23:27



Mercurial est presque entièrement écrit en python. Le noyau de Git est écrit en C (et devrait être plus rapide que celui de Mercurial) et les outils écrits en sh, perl, tcl et utilise des utilitaires GNU standard. Ainsi, il doit amener tous ces utilitaires et interprètes avec le système qui ne les contient pas (par exemple Windows).

Les deux soutiennent le travail avec SVN, bien que le support d'AFAIK svn soit cassé pour git sur Windows (peut être je suis juste malchanceux / boiteux, qui sait). Il y a aussi des extensions qui permettent d'interopérer entre git et Mercurial.

Mercurial a bien Intégration de Visual Studio. La dernière fois que j'ai vérifié, plugin pour Git travaillait mais extrêmement lent.

Les ensembles de commandes de base sont très similaires (init, clone, add, status, commit, push, pull etc.). Le flux de travail de base sera donc le même. En outre, il y a un client semblable à TortoiseSVN pour les deux.

Les extensions pour Mercurial peuvent être écrites en python (pas de surprise!) Et pour git elles peuvent être écrites sous n'importe quelle forme exécutable (binaire exécutable, script shell etc.). Certaines extensions sont puissantes, comme git bisect.


11
2017-10-21 05:13



Si vous avez besoin d'un bon support Windows, vous préférerez peut-être Mercurial. TortoiseHg (Windows Explorer plugin) parvient à offrir une interface graphique simple à utiliser pour un outil assez complexe. Comme état ici, vous aurez également un Plug-in Visual Studio. Cependant, la dernière fois que j'ai essayé, l'interface SVN ne fonctionnait pas très bien sur Windows.

Si cela ne vous dérange pas l'interface de ligne de commande, je recommanderais Git. Pas pour des raisons techniques mais pour une stratégie. Le taux d'adoption de git est beaucoup plus haute. Voyez combien de projets open source célèbres passent de cvs / svn à Mercurial et combien passent à Git. Voyez combien de fournisseurs d'hébergement de code / projet vous pouvez trouver avec le support git par rapport à l'hébergement Mercurial.


11
2017-10-21 23:50