web-dev-qa-db-fra.com

Git et Mercurial - Comparaison et contraste

Cela fait un moment que j'utilise Subversion pour mes projets personnels.

De plus en plus, j'entends de grandes choses sur Git et Mercurial, et sur DVCS en général.

J'aimerais faire le tour de la question des systèmes DVCS, mais je ne connais pas ces options.

Quelles sont les différences entre Mercurial et Git?

Remarque: je pas essaie de savoir lequel est le "meilleur" ou même celui par lequel je devrais commencer. Je recherche principalement des domaines clés dans lesquels ils sont similaires et dans lesquels ils sont différents, car je suis intéressé de savoir en quoi ils diffèrent en termes de mise en œuvre et de philosophie.

516
TM.

Disclaimer:J'utilise Git, suis le développement de Git sur la liste de diffusion git, et contribue même un peu à Git (principalement gitweb). Je connais Mercurial grâce à la documentation et à des discussions sur le canal #revctrl IRC sur FreeNode.

Merci à toutes les personnes de la chaîne #Mercurial IRC qui ont fourni de l'aide sur Mercurial pour cet article



Sommaire

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

  • Structure du référentiel: Mercurial n'autorise pas les fusions de poulpes (avec plus de deux parents), ni le balisage d'objets non-commit.
  • Tags: Mercurial utilise un fichier versionné .hgtags Avec des règles spéciales pour les tags par référentiel. Il prend également en charge les tags locaux dans .hg/localtags; Les balises Git sont des références résidant dans un espace de noms refs/tags/. Par défaut, elles sont auto-suivies lors de la récupération et nécessitent un transfert explicite.
  • Branches: Dans Mercurial, le processus de travail de base est basé sur têtes anonymes; Git utilise des branches nommées légères et a des types de branches spéciaux (branches de suivi à distance) qui suivent les branches du référentiel distant.
  • Nomenclature et plages de révision: 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 cette numérotation local; Git fournit un moyen de faire référence à la révision par rapport à la pointe de la branche, et les plages de révision sont topologiques (basées sur un graphique de révisions)
  • Mercurial utilise renommer le suivi, tandis que Git utilise rename détection pour gérer les renommage de fichiers
  • Réseau: Mercurial prend en charge les protocoles "intelligents" SSH et HTTP, ainsi que le protocole HTTP statique; Git moderne prend en charge les protocoles "intelligents" SSH, HTTP et GIT, ainsi que le protocole "muet" HTTP (S). Les deux prennent en charge les fichiers de lots pour le transport hors ligne.
  • Mercurial utilise extensions (plugins) et une API établie; Git a scriptability et des formats établis.

Il y a quelques choses qui diffèrent Mercurial de Git, mais il y a d'autres choses qui les rendent similaires. Les deux projets empruntent des idées l'un à l'autre. Par exemple, la commande hg bisect Dans Mercurial (anciennement extension de la bissect ) était inspirée par la commande git bisect Dans Git, tandis que l’idée de git bundle Était inspirée de hg bundle.

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

Dans sa base de données d’objets, Git contient quatre types d’objets: les objets blob qui contiennent le contenu d’un fichier, les objets hiérarchiques tree qui stockent la structure de répertoires, y compris les noms de fichiers et les parties correspondantes des autorisations de fichiers (autorisations exécutables). pour les fichiers, en tant que lien symbolique), objet commit contenant des informations sur l'auteur, pointeur sur l'instantané de l'état du référentiel à la révision représentée par une validation (via un objet arborescence du répertoire principal du projet) et références à zéro ou plus parent valide et objets tag faisant référence à d'autres objets et pouvant être signés à l'aide de PGP/GPG.

Git utilise deux manières de stocker des objets: le format loose, dans lequel chaque objet est stocké dans un fichier séparé (ces fichiers sont écrits une fois, et jamais modifié), et le format emballé dans lequel de nombreux objets sont stockés. compressé 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 la méthode créer + renommer) après avoir écrit un objet.

Les référentiels Git nécessitent une maintenance périodique à l'aide de git gc (Pour réduire l'espace disque et améliorer les performances), bien que Git le fasse automatiquement de nos jours. (Cette méthode permet une meilleure compression des référentiels.)

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

Mercurial utilise transaction journal pour fournir des informations sur les opérations et s'appuie sur les fichiers truncating pour les nettoyer après un échec ou une interruption de l'opération. Les Revlogs sont uniquement ajoutés.

En regardant la structure du référentiel dans Git par rapport à Mercurial, on peut voir que Git ressemble plus à une base de données d'objet (ou à un système de fichiers à contenu adressé) et Mercurial à une base de données relationnelle à champ fixe traditionnelle.

Différences:
Dans Git, les objets tree forment une structure hierarchical; dans Mercurial manifest, le fichier est une structure flat. Dans le magasin d'objets Git blobne version du contenu d'un fichier; dans Mercurial filelog stocke historique complet d'un seul fichier (si nous ne tenons pas compte ici des complications éventuelles avec le changement de nom). Cela signifie qu’il existe différents domaines d’opérations dans lesquels Git serait plus rapide que Mercurial, toutes choses égales par ailleurs (telles que la fusion ou l’historique d’un projet), et les domaines dans lesquels Mercurial serait plus rapide que Git (comme appliquer des correctifs ou afficher historique d'un seul fichier). Ce problème n'est peut-être pas important pour l'utilisateur final.

En raison de la structure à enregistrement fixe de la structure changelog de Mercurial, les commits dans Mercurial ne peuvent avoir que jusqu'à deux parents; Les commits dans Git peuvent avoir plus de deux parents (appelé "fusion de pieuvre"). Bien que vous puissiez (en théorie) remplacer la fusion de poulpes par une série de fusions à deux parents, cela pourrait entraîner des complications lors de la conversion entre les référentiels Mercurial et Git.

Pour autant que je sache, Mercurial n’a pas d’équivalent de balises annotées (objets balises) de Git. Un cas particulier de balises annotées est balises signées (avec signature PGP/GPG); l’équivalent dans Mercurial peut être fait en utilisant GpgExtension , quelle extension est distribuée avec Mercurial. Vous ne pouvez pas tag non-commit object ​​dans Mercurial comme dans Git, mais ce n'est pas très important, je pense (certains référentiels git utilisent tagblob blob pour distribuer la clé publique PGP à utiliser pour vérifier Mots clés).

Références: branches et tags

Dans Git, les références (branches, branches de suivi distantes et balises) se trouvent en dehors du DAG des commits (comme il se doit). Les références dans l'espace de noms refs/heads/ (branches locales) pointent vers les commits et sont généralement mises à jour par "git commit"; ils pointent vers la pointe de la branche, c'est pourquoi ce nom-là. Les références dans l'espace de noms refs/remotes/<remotename>/ ([branches de suivi à distance]) indiquent la validation, suivent les branches du référentiel distant <remotename> Et sont mises à jour par "git fetch" ou son équivalent. Les références dans l'espace de noms refs/tags/ (tags) désignent généralement des commits (balises légères) ou des objets de balise (balises annotées et signées) et ne sont pas censés changer.

Mots clés

Dans Mercurial, vous pouvez donner un nom persistant à la révision en utilisant tag; les étiquettes sont stockées de la même manière que les modèles ignorés. Cela signifie que les balises globalement visibles sont stockées dans le fichier .hgtags Contrôlé par la révision de votre référentiel. Cela a deux conséquences: premièrement, Mercurial doit utiliser des règles spéciales pour que ce fichier obtienne 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, mais pas la version actuellement extraite); deuxièmement, vous devez valider les modifications apportées à ce fichier pour que la nouvelle balise soit visible par les autres utilisateurs/autres référentiels (pour autant que je sache).

Mercurial prend également en charge les balises locales, stockées dans hg/localtags, Qui ne sont pas visibles par les autres (et ne sont bien sûr pas transférables).

Dans Git, les tags sont des références fixes (constantes) à d'autres objets (généralement des objets de tags, qui à leur tour pointent vers des commits) stockés dans un espace de noms refs/tags/. Par défaut, lors de l'extraction ou de la poussée d'un ensemble de révisions, git extrait ou pousse automatiquement les balises qui indiquent les révisions en cours d'extraction ou de poussée. Néanmoins, vous pouvez contrôler dans une certaine mesure quelles balises sont récupérées ou poussées.

Git traite les balises légères (pointant directement vers les commits) et les balises annotées (pointant sur les objets de balise, qui contiennent le message de balise qui inclut éventuellement la signature PGP, qui pointe à son tour à valider) légèrement différemment, par exemple, par défaut, il considère uniquement les balises annotées lors de la description commets en utilisant "git decrire".

Git n'a pas d'équivalent strict des balises locales dans Mercurial. Néanmoins, les meilleures pratiques de git recommandent de configurer un référentiel nu public distinct, dans lequel vous apportez les modifications Push ready et à partir duquel les autres clonent et récupèrent. Cela signifie que les balises (et branches) que vous n'avez pas Push sont privées de votre référentiel. D'autre part, vous pouvez également utiliser un espace de noms autre que heads, remotes ou tags, par exemple local-tags Pour les balises locales.

Opinion personnelle: À mon avis, les balises doivent résider en dehors du graphe de révision, car elles lui sont externes (ce sont des pointeurs sur le graphe de révisions). Les balises doivent être non versionnées, mais transférables. Le choix par Mercurial d'utiliser un mécanisme similaire à celui utilisé pour ignorer les fichiers signifie qu'il doit soit traiter .hgtags De manière spécifique (le fichier dans l'arborescence est transférable, mais il est généralement versionné), ou avoir des balises qui sont uniquement locales (.hg/localtags N'est pas versionné, mais ne peut être transféré).

Branches

Dans Git branche locale (astuce de branche, ou tête de branche) est une référence nommée à un commit, dans lequel on peut développer de nouveaux commits. Par branche, on entend également une ligne de développement active, c’est-à-dire que tous les engagements sont accessibles depuis la pointe de la branche. Les branches locales résident dans l’espace de noms refs/heads/, Par exemple. Le nom complet de la branche 'master' est 'refs/heads/master'.

La branche actuelle dans Git (ce qui signifie une branche extraite et une branche vers laquelle la nouvelle validation ira) est la branche référencée par le HEAD ref. On peut avoir HEAD pointant directement sur un commit, plutôt que d'être une référence symbolique; cette situation d'appartenance à une branche anonyme sans nom est appelée détachée HEAD ("branche git" indique que vous êtes sur '(pas de branche)').

Dans Mercurial, il existe des branches anonymes (têtes de branches), et on peut utiliser des signets (via extension du signet ). Ces branches de signets sont purement locales et ces noms étaient (jusqu’à la version 1.6) non transférables avec Mercurial. Vous pouvez utiliser rsync ou scp pour copier le fichier .hg/bookmarks Dans un référentiel distant. Vous pouvez également utiliser hg id -r <bookmark> <url> Pour obtenir l'identifiant de révision de la pointe actuelle d'un signet.

Depuis 1,6, les signets peuvent être poussés/tirés. La page BookmarksExtension contient une section sur tilisation de référentiels distants . Il existe une différence en ce que les noms de signets Mercurial sont global, tandis que la définition de 'distant' dans Git décrit également le mappage des noms de branche à partir des noms du référentiel distant vers les noms des branches locales de suivi à distance ; par exemple, la correspondance refs/heads/*:refs/remotes/Origin/* signifie que l’on peut trouver l’état de la branche 'maître' ('refs/heads/master') dans le référentiel distant de la branche de suivi à distance 'Origin/master' ('refs/remotes/Origine/maître ').

Mercurial a aussi appelé named branches, où le nom de la branche est embedded dans une validation (dans un changeset). Ce nom est global (transféré lors de l'extraction). Ces noms de branche sont enregistrés de manière permanente dans le cadre des métadonnées changeset. Avec Mercurial moderne, vous pouvez fermer "branche nommée" et arrêter l'enregistrement du nom de la branche. Dans ce mécanisme, les bouts de branches sont calculés à la volée.

Les "branches nommées" de Mercurial devraient à mon avis être appelées commit labels, car c'est ce qu'elles sont. Il existe des situations où "branche nommée" peut avoir plusieurs astuces (plusieurs validations sans enfant) et peut également consister en plusieurs parties disjointes d'un graphique de révisions.

Il n'y a pas d'équivalent de ces "branches incorporées" Mercurial dans Git; De plus, la philosophie de Git est que, même si on peut dire que la branche inclut un commit, cela ne veut pas dire qu'un commit appartient à une branche.

Notez que la documentation Mercurial propose toujours d'utiliser des clones distincts (référentiels distincts) au moins pour les branches à longue durée de vie (flux de travaux branche par dépôt), également appelé branchement par clonage.

Branches en poussant

Mercurial par défaut pousse toutes les têtes. Si vous voulez pousser une seule branche (single head), vous devez spécifier la révision de la branche que vous voulez pousser. Vous pouvez spécifier le conseil de la branche par son numéro de révision (local au référentiel), par son identifiant, par nom de signet (local à référentiel, non transféré) ou par nom de branche incorporée (nommée branche).

Autant que je sache, si vous soumettez une série de révisions contenant des validations marquées comme se trouvant sur une "branche nommée" dans le jargon Mercurial, vous aurez cette "branche nommée" dans le référentiel vers lequel vous appuyez. Cela signifie que les noms de telles branches incorporées ("branches nommées") sont global (en ce qui concerne les clones d'un référentiel/projet donné).

Par défaut (sujet à la variable de configuration Push.default) "Git Push" ou "git Push <éloigné> "Git Push Push branches correspondantes, c'est-à-dire uniquement les branches locales qui ont déjà leur équivalent dans le référentiel distant vers lequel vous poussez. Vous pouvez utiliser l'option --all Pour git-Push (" git Push --all ") à Push toutes les branches, vous pouvez utiliser" git Push <éloigné> <branche> "Pousser une branche unique donnée], et vous pouvez utiliser" git Push <éloigné> HEAD "à pousser branche actuelle.

Tout ce qui précède suppose que Git n'est pas configuré pour se connecter aux variables de configuration Push remote.<remotename>.Push.

Branches à chercher

Note: Ici, j'utilise la terminologie Git où "fetch" signifie télécharger les modifications depuis le référentiel distant sans intégrer ces modifications au travail local. C'est ce que font "git fetch" Et "hg pull".

Si je comprends bien, par défaut, Mercurial extrait toutes les têtes 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>" Pour obtenir ne seule branche. Vous pouvez spécifier <rev> à l'aide de l'identifiant de révision, du nom "branche nommée" (branche incorporée dans changelog) ou du nom du signet. Le nom du signet cependant (du moins actuellement) n'est pas transféré. Toutes les révisions de "branches nommées" auxquelles vous appartenez sont transférées. "hg pull" stocke les pointes des branches qu'il a récupérées sous forme de têtes anonymes et non nommées.

Dans Git par défaut (pour la télécommande 'Origin' créée par "git clone" et pour les télécommandes créées avec "git remote add") "git fetch" (Ou "git fetch <remote>") - toutes les branches depuis le référentiel distant (de l'espace de noms refs/heads/) et les stocke dans l'espace de noms refs/remotes/. Cela signifie par exemple que la branche nommée 'maître' (nom complet: 'refs/heads/master') sur la 'distance' d'origine 'distante serait stockée (enregistrée) sous le nom' branche/maître 'branche de suivi à distance (nom complet : 'refs/télécommandes/origine/maître').

Vous pouvez récupérer single branch dans Git en utilisant git fetch <remote> <branch> - Git stockera les branches demandées dans FETCH_HEAD, qui est quelque chose de similaire aux têtes non nommées Mercurial.

Ce ne sont là 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 voulez extraire et où les stocker. Par exemple, la valeur par défaut "fetch all branches" est représentée par le caractère générique '+ refs/heads/*: refs/remotes/Origin/*', et "fetch single branch" est un raccourci pour "refs/heads/<branche>:" . Les références référentielles sont utilisées pour mapper les noms de branches (références) dans le référentiel distant aux noms de références locales. Mais vous n’avez pas besoin d’en savoir beaucoup sur les refspecs pour pouvoir travailler efficacement avec Git (principalement grâce à la commande "git remote").

Opinion personnelle: Personnellement, je pense que les "branches nommées" (avec les noms de branche incorporés dans les métadonnées de l'ensemble de modifications) dans Mercurial constituent une conception erronée avec son espace de noms global, en particulier pour un système de contrôle de version Distributed. Par exemple, prenons le cas où Alice et Bob ont tous deux "une branche nommée" nommée "for-joe" dans leur référentiel, des branches qui n'ont rien en commun. Dans le référentiel de Joe, cependant, ces deux branches seraient maltraitées comme une seule branche. Vous avez donc en quelque sorte mis en place une convention protégeant contre les conflits de noms de branches. Ce n'est pas un problème avec Git, où dans le référentiel de Joe 'for-joe' d'Alice serait 'alice/for-joe' et de Bob, ce serait 'bob/for-joe'. Voir aussi Séparer le nom de la branche de l'identité de la branche problème soulevé sur Mercurial wiki.

Les "branches de marque-pages" de Mercurial sont actuellement dépourvues de mécanisme de distribution interne.

Différences:
Cette région est l’une des principales différences entre Mercurial et Git, comme james woodyatt et Steve Losh dans leurs réponses. Mercurial utilise par défaut des lignes de code anonymes et légères, appelées dans leur terminologie "têtes". Git utilise des branches nommées légères, avec un mappage par injection pour mapper les noms des branches du référentiel distant aux noms des branches de suivi à distance. Git "vous oblige" à nommer les branches (enfin, à l'exception d'une branche non nommée, situation appelée HEAD détachée), mais je pense que cela fonctionne mieux avec des flux de travaux contenant beaucoup de branches, tels que le flux de travaux de branche thématique, ce qui signifie plusieurs branches dans le paradigme de référentiel unique.

Nommer les révisions

Dans Git, il existe de nombreuses manières de nommer les révisions (décrites par exemple dans git rev-parse page de manuel):

  • Nom d'objet SHA1 complet (chaîne hexadécimale de 40 octets) ou sous-chaîne unique dans le référentiel
  • Un nom de référence symbolique, par exemple 'master' (en référence à la branche 'master') ou 'v1.5.0' (en référence à la balise), ou 'Origin/next' (en référence à la branche de suivi à distance)
  • Un suffixe ^ Au paramètre révision signifie le premier parent d'un objet de validation, ^n Signifie le nième parent d'une validation de fusion. Un suffixe ~n Pour le paramètre de révision signifie le n-ème ancêtre d'un commit en ligne droite première-parent. Ces suffixes peuvent être combinés pour former un spécificateur de révision suivant le chemin à partir d'une référence symbolique, par ex. 'pu ~ 3 ^ 2 ~ 3'
  • La sortie de "git describe", c'est-à-dire une balise la plus proche, éventuellement suivie d'un tiret et de plusieurs commits, suivie d'un tiret, d'un "g" et d'un nom d'objet abrégé, par exemple "v1.6.5.1-75- g5bf8097 '.

Il existe également des spécificateurs de révision impliquant reflog, non mentionnés ici. Dans Git, chaque objet, que ce soit commit, tag, tree ou blob, a son identifiant SHA-1; il y a une syntaxe spéciale comme par exemple 'next: Documentation' ou 'next: README' pour désigner une arborescence (répertoire) ou un blob (contenu du fichier) à la révision spécifiée.

Mercurial a également plusieurs façons de nommer les changesets (décrits par exemple dans hg page de manuel):

  • Un entier simple est traité comme un numéro de révision. Il faut se rappeler que les numéros de révision sont locaux pour un référentiel donné; dans un autre référentiel, ils peuvent être différents.
  • Les entiers négatifs sont traités comme des décalages séquentiels à partir de la pointe, -1 désignant la pointe, -2 la révision antérieure à la pointe, etc. Ils sont aussi local au dépôt.
  • Un identifiant de révision unique (chaîne hexadécimale de 40 chiffres) ou son préfixe unique.
  • Un nom de tag (nom symbolique associé à une révision donnée), ou un nom de signet (avec extension: nom symbolique associé à une tête donnée, local au référentiel), ou une "branche nommée" (étiquette de validation; révision donnée par "branche nommée") tip (commit sans enfant) de tous les commits avec une étiquette de commit donnée, avec le plus grand numéro de révision s'il y en a plus d'un)
  • Le nom réservé "tip" est une balise spéciale qui identifie toujours la révision la plus récente.
  • Le nom réservé "null" indique la révision null.
  • Le nom réservé "." indique le parent du répertoire de travail.

Différences
Comme vous pouvez le constater en comparant les listes ci-dessus, Mercurial propose des numéros de révision locaux à un référentiel, contrairement à Git. D'autre part, Mercurial n'offre des compensations relatives qu'à partir de 'tip' (branche actuelle), qui sont locales au référentiel (au moins sans ParentrevspecExtension ), tandis que Git permet de spécifier toute validation ultérieure de n'importe quelle astuce.

La révision la plus récente s'appelle HEAD dans Git et "pointe" dans Mercurial; il n'y a pas de révision nulle dans Git. Mercurial et Git peuvent avoir plusieurs racines (peuvent avoir plusieurs commits sans parent; cela résulte généralement de l'adhésion de projets auparavant séparés).

Voir aussi:Plusieurs types de spécificateurs de révision article sur le blog d'Elijah (newren).

Opinion personnelle: Je pense que les numéros de révision sont surestimés (du moins pour le développement distribué et/ou l'historique non linéaire/à branches). Premièrement, pour un système de contrôle de version distribué, ils doivent être soit locaux au référentiel, soit exiger que certains référentiels soient traités de manière spéciale en tant qu'autorité de numérotation centrale. Deuxièmement, les projets plus volumineux, avec un historique plus long, peuvent avoir un nombre de révisions compris dans une plage de 5 chiffres, de sorte qu’ils n’offrent qu’un léger avantage par rapport aux identifiants de révision abrégés à 6 ou 7 caractères, et impliquent un ordre strict alors les révisions n et n + 1 ne doivent pas nécessairement être parent et enfant).

Plages de révision

Dans Git, les plages de révision sont topologiques. La syntaxe couramment vue A..B, Qui pour l'historique linéaire signifie une plage de révision commençant par A (à l'exclusion de A) et se terminant par B (c'est-à-dire que la plage est ouverte de dessous), est abrégée ("sucre syntaxique" ) pour ^A B, qui signifie pour toutes les commandes parcourant l’historique accessibles de B, à l’exception de ceux accessibles de A. Cela signifie que le comportement de la plage A..B est tout à fait prévisible (et très utile) même si A n'est pas l'ancêtre de B: A..B Signifie alors la plage de révisions de l'ancêtre commun de A et B (base de fusion) à la révision B.

Dans Mercurial, les plages de révision sont basées sur numéros de révision. La plage est spécifiée à l'aide de la syntaxe A:B, Et contrairement à la plage Git, elle agit comme un intervalle fermé. Aussi la plage B: A est la plage A: B dans l’ordre inverse, ce qui n’est pas le cas dans Git (voir toutefois la remarque ci-dessous sur la syntaxe A...B). Mais cette simplicité a un prix: la plage de révision A: B n’a de sens que si A est l’ancêtre de B ou inversement, c’est-à-dire avec un historique linéaire; sinon (je suppose que) la plage est imprévisible et le résultat est local dans le référentiel (car les numéros de révision sont locaux dans le référentiel).

Ceci est corrigé avec Mercurial 1.6, qui a une nouvelle plage de révision topologique, où 'A..B' (ou 'A :: B') est compris comme l'ensemble des changesets qui descendent tous deux de X et les ancêtres de Y. Ceci est, je suppose, équivalent à '--ancestry-path A..B' dans Git.

Git a aussi la notation A...B Pour la différence symétrique des révisions; cela signifie A B --not $(git merge-base A B), ce qui signifie que tous les engagements sont accessibles depuis A ou B, mais en excluant tous les engagements accessibles depuis les deux (accessible depuis des ancêtres communs).

Renomme

Mercurial utilise rename tracking pour gérer les renommements de fichiers. Cela signifie que les informations sur le fait qu'un fichier a été renommé sont enregistrées au moment de la validation. dans Mercurial, ces informations sont enregistrées sous la forme "Enhanced Diff" dans les métadonnées filelog (file revlog). La conséquence en est que vous devez utiliser hg rename/hg mv ... ou vous devez vous rappeler d'exécuter hg addremove Pour effectuer la détection de renommage basée sur la similarité.

Git est unique parmi les systèmes de contrôle de version en ce sens qu'il utilise détection du renommage pour traiter les renommage de fichiers. Cela signifie que le fait que le fichier a été renommé est détecté au moment voulu: lors d'une fusion ou lors de l'affichage d'un diff (si demandé/configuré). Cela présente l'avantage que l'algorithme de détection de changement de nom peut être amélioré et qu'il n'est pas gelé au moment de la validation.

Git et Mercurial nécessitent l’utilisation de l’option --follow Pour suivre les renomations lors de l’affichage de l’historique d’un seul fichier. Les deux peuvent suivre les renommage lorsqu'ils affichent l'historique ligne par fichier d'un fichier dans git blame/hg annotate.

Dans Git, la commande git blame Permet de suivre le mouvement du code, mais aussi de déplacer (ou de copier) le code d'un fichier à l'autre, même si le mouvement de code ne fait pas partie d'un changement de nom de fichier complet. Autant que je sache, cette fonctionnalité est propre à Git (au moment de la rédaction de cet article, octobre 2009).

Protocoles de réseau

Mercurial et Git prennent en charge l'extraction et le repérage de référentiels sur le même système de fichiers, où l'URL du référentiel est simplement un chemin de système de fichiers menant au référentiel. Les deux prennent également en charge la récupération de fichiers bundle.

Mercurial prend en charge la récupération et la diffusion via les protocoles SSH et HTTP. Pour SSH, il faut un compte Shell accessible sur la machine de destination et une copie de hg installé/disponible. Pour un accès HTTP, le script CGI hg-serve Ou Mercurial doit être exécuté et Mercurial doit être installé sur la machine du serveur.

Git prend en charge deux types de protocoles utilisés pour accéder au référentiel distant:

  • Les protocoles "intelligents", qui incluent l'accès via SSH et via le protocole personnalisé git: // (de git-daemon), nécessitent l'installation de git sur le serveur. L’échange dans ces protocoles consiste en un échange entre client et serveur sur les objets qu’ils ont en commun, puis en générant et en envoyant un fichier de paquet. Modern Git prend en charge le protocole HTTP "intelligent".
  • Les protocoles "dumb", qui incluent HTTP et FTP (uniquement pour la récupération), et HTTPS (pour le transfert via WebDAV), ne nécessitent pas l'installation de git sur le serveur, mais nécessitent que le référentiel contienne des informations supplémentaires générées par git update-server-info (Généralement exécuté à partir d'un crochet). L’échange consiste en un client marchant dans la chaîne de validation et en téléchargeant des objets et des fichiers pack non compressés selon les besoins. L’inconvénient est qu’il télécharge plus que ce qui est strictement nécessaire (par exemple, dans les cas où il n’existe qu’un fichier unique, il serait téléchargé intégralement même lorsqu’il ne récupèrerait que quelques révisions), et qu’il faudrait de nombreuses connexions pour terminer.

Extension: possibilité de script contre extensions (plugins)

Mercurial est implémenté dans Python, avec un code de base écrit en C pour la performance. Il fournit une API pour écrire extensions (plugins) comme moyen d'ajouter des fonctionnalités supplémentaires. Certaines fonctionnalités, telles que les "branches de signets" ou les révisions de signature, sont fournies dans les extensions distribuées avec Mercurial et doivent être activées.

Git est implémenté dans C , Perl et scripts de shell. Git fournit de nombreuses commandes de bas niveau (plumbing) pouvant être utilisées dans les scripts. La manière habituelle d’introduire une nouvelle fonctionnalité est de l’écrire sous forme de script Perl ou Shell, puis, lorsque l’interface utilisateur se stabilise, de la réécrire en C pour en améliorer les performances, la portabilité et, dans le cas d’un script Shell, éviter les cas imprécis (cette procédure est appelée builtinification).

Git s'appuie sur et repose sur les formats [référentiel] et les protocoles [réseau]. Au lieu des liaisons de langage, il y a (partiel ou complet) reimplementations de Git dans d'autres langages (certains sont partiellement des réimplémentations et des enveloppeurs autour de commandes git): JGit (Java, utilisé par EGit, Eclipse Git Plugin), Grit (Ruby), Dulwich (Python), git # (C #).


TL; DR

446
Jakub Narębski

Je pense que vous pouvez avoir une idée de la similitude ou de la différence entre ces systèmes et 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. D'après ce que je comprends de Git, une chose importante à la différence de Git est qu'il suit le contenu des fichiers plutôt que les fichiers eux-mêmes. Linus dit que si vous déplacez une fonction d'un fichier à un autre, Git vous expliquera l'historique de cette fonction lors du déplacement.

Ils disent aussi que git est plus lent sur HTTP mais qu’il possède son propre protocole réseau et son propre serveur.

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

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

56
artemb

Il y a quelque temps, j'ai écrit un article sur les modèles de branchement de Mercurial et 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/

30
Steve Losh

J'utilise les deux assez régulièrement. La principale différence fonctionnelle réside dans la façon dont les noms Git et Mercurial se ramifient dans les référentiels. Avec Mercurial, les noms de branche sont clonés et extraits avec leurs changesets. Lorsque vous ajoutez des modifications à une nouvelle branche dans Mercurial et que vous les transmettez à un autre référentiel, le nom de la branche est transféré au même moment. Ainsi, les noms de branche sont plus ou moins globaux dans Mercurial, et vous devez utiliser l'extension Bookmark pour avoir des noms légers uniquement locaux (si vous les voulez; Mercurial, par défaut, utilise des codelines légers anonymes, qui dans sa terminologie sont: appelé "têtes"). Dans Git, les noms de branche et leur mappage par injection avec des branches distantes sont stockés localement et vous devez les gérer explicitement, ce qui signifie savoir comment faire cela. C'est à peu près à cela que Git tire sa réputation d'être plus difficile à apprendre et à utiliser que Mercurial.

Comme d'autres le noteront ici, il existe de nombreuses différences mineures. La chose avec les branches est le grand différentiateur.

25
james woodyatt

Jetez un oeil à Git vs. Mercurial: Relaxez-vous blogueur 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.

19
Jakub Narębski

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 en sh, Perl, tcl et utilise le standard GNU utils. Il doit donc amener tous ces utils et interprètes avec à un système qui ne les contient pas (par exemple Windows).

Les deux supportent le travail avec SVN, bien que le support AFAIK svn soit cassé pour git sous Windows (peut-être que je suis juste malchanceux/boiteux, qui sait). Il existe également des extensions qui permettent d'interagir entre git et Mercurial.

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

Les jeux de commandes de base sont très similaires (init, clone, add, status, commit, Push, pull, etc.). Ainsi, le flux de travail de base sera le même. En outre, il existe 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 quel format exécutable (binaire exécutable, script shell, etc.). Certaines extensions sont incroyablement puissantes, comme git bisect.

11
elder_george

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

Si l'interface de ligne de commande ne vous dérange pas, je vous recommanderais Git. Pas pour des raisons techniques mais pour des raisons stratégiques. Le taux d'adoption de git est beaucoup plus élevé . Il suffit de voir 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
Eric Darchis

Après avoir lu partout que Mercurial est plus facile (ce que je pense toujours, après l'avis de la communauté Internet), lorsque j'ai commencé à travailler avec Git et Mercurial, j'ai senti que Git était relativement plus simple à adapter pour moi (j'ai commencé avec Mercurial avec TortoiseHg) lorsque vous utilisez la ligne de commande, principalement parce que les commandes git ont été nommées de manière appropriée selon moi et sont moins nombreuses. Mercurial a des noms différents. nommer pour chaque commande qui effectue un travail distinct, alors que les commandes Git peuvent être polyvalentes en fonction de la situation (par exemple, checkout). Alors que Git était plus difficile à l'époque, la différence est maintenant minime. YMMV .. Avec un bon client d'interface graphique tel que TortoiseHg, il était vrai qu'il était beaucoup plus facile de travailler avec Mercurial et je n'avais pas à me souvenir des commandes légèrement déroutantes. Je ne vais pas entrer dans les détails de la façon dont chaque commande pour la même action varie, mais voici deux listes complètes: 1 du propre site de Mercurial et 2e 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 enregistre un enregistrement de chaque version des fichiers validés en interne, tandis que Hg enregistre uniquement les ensembles de modifications qui peuvent avoir une empreinte plus petite. Git facilite le changement d’historique par rapport au Hg, mais c’est encore une caractéristique hate-it-or-love-it. J'aime Hg pour ancien et Git pour ce dernier.

Ce qui me manque dans Hg, c'est la fonctionnalité de sous-module de Git. Hg a des subrepos mais ce n'est pas exactement un sous-module Git.

L’écosystème autour des deux peut également influencer le choix de chacun: Git doit être plus populaire (mais c’est trivial), Git a GitHub alors que Mercurial a BitBucket , Mercurial a TortoiseHg pour lequel je n'ont pas vu d'équivalent aussi bon pour Git.

Chacune a ses avantages et ses inconvénients, que vous ne perdiez ni l'un ni l'autre.

11
nawfal

Départ post de Scott Chacon depuis un certain temps.

Je pense que git a la réputation d'être "plus compliqué", bien que, d'après mon expérience, ce ne soit pas plus compliqué que nécessaire. IMO, le modèle git est plus facile à comprendre (les balises contiennent des commits (et les pointeurs vers zéro commité parent) contiennent des arbres contenant des blobs et d’autres arbres. .. terminé).

Selon mon expérience, git n’est pas plus déroutant que Mercurial. Je recommande de lire à nouveau ce billet de Scott Chacon sur le sujet.

8
Dustin

J'ai utilisé Git pendant un peu plus d'un an à mon emploi actuel et auparavant, à Mercurial, pendant un peu plus d'un an à mon emploi précédent. Je vais fournir une évaluation du point de vue de l'utilisateur.

Premièrement, les deux systèmes sont des systèmes de contrôle de version distribués. Les systèmes de contrôle de version distribués requièrent un changement de mentalité par rapport aux systèmes de contrôle de version traditionnels, mais fonctionnent beaucoup mieux à bien des égards une fois que l’on les comprend. Pour cette raison, je considère que Git et Mercurial sont bien supérieurs à Subversion, Perforce, etc. La différence entre les systèmes de contrôle de version distribuée et les systèmes de contrôle de version traditionnels est beaucoup plus grande que celle entre Git et Mercurial.

Cependant, il existe également des différences significatives entre Git et Mercurial qui rendent chaque système plus adapté à son propre sous-ensemble de cas d'utilisation.

Mercurial est plus simple à apprendre. Je suis arrivé au point où je devais rarement faire référence à de la documentation ou à des notes après quelques semaines d'utilisation de Mercurial; Je dois toujours consulter mes notes régulièrement avec Git, même après l'avoir utilisée pendant un an. Git est considérablement plus compliqué.

C'est en partie parce que Mercurial est tout simplement plus propre. Il est rare que vous deviez créer des branches manuellement dans Mercurial; Mercurial créera automatiquement une branche anonyme pour vous si et quand vous en avez besoin. La nomenclature Mercurial est plus intuitive. vous n'avez pas à vous soucier de la différence entre "chercher" et "tirer" comme vous le faites avec Git. Mercurial est un peu moins bogué. Des problèmes de respect de la casse des noms de fichiers étaient à l’origine des problèmes rencontrés lors du transfert de projets sur plusieurs plates-formes avec Git et Mercurial; cela a été corrigé dans Mercurial il y a quelque temps alors que cela n’avait pas été corrigé dans Git. Vous pouvez indiquer à Mercurial le renommage de fichiers; avec Git, s’il ne détecte pas automatiquement le changement de nom - une proposition très aléatoire selon mon expérience - le changement de nom ne peut pas être suivi du tout.

L'autre raison de la complication supplémentaire de Git, cependant, est qu'une grande partie de celle-ci est nécessaire pour prendre en charge des fonctionnalités et une alimentation supplémentaires. Oui, c'est plus compliqué de gérer les branches dans Git - mais d'un autre côté, une fois que vous avez les branches, il n'est pas trop difficile de faire des choses avec ces branches qui sont pratiquement impossibles dans Mercurial. La redistribution des branches est l’une des choses suivantes: vous pouvez déplacer votre branche de sorte que sa base, au lieu d’être l’état du tronc lorsque vous branchez, soit maintenant l’état du tronc; Cela simplifie grandement l’historique des versions lorsque de nombreuses personnes travaillent sur la même base de code, puisque chacune des pressions sur le tronc peut apparaître séquentiellement plutôt que étroitement liée. De même, il est beaucoup plus facile de réduire plusieurs commits de votre branche en un seul, ce qui peut à nouveau vous aider à garder l'historique de contrôle de version propre. commits et sous-branches que le développeur peut avoir créés lors du développement de la fonctionnalité.

En définitive, je pense que le choix entre Mercurial et Git devrait dépendre de la taille de vos projets de contrôle de versions, mesurée en nombre de personnes travaillant simultanément sur ceux-ci. Si vous avez un groupe d'une dizaine de personnes ou plus travaillant sur une seule application Web monolithique, par exemple, les outils de gestion de succursale plus puissants de Git en feront un bien meilleur pour votre projet. D'autre part, si votre équipe développe un système distribué hétérogène, avec seulement un ou deux développeurs travaillant simultanément sur un composant, utiliser un référentiel Mercurial pour chacun des projets de composants permettra au développement de se dérouler de manière plus fluide avec moins de ressources. surcharge de gestion du référentiel.

En bout de ligne: si vous avez une grande équipe développant une seule application énorme, utilisez Git; Si vos applications individuelles sont petites, avec une échelle quelconque provenant du nombre plutôt que de la taille de telles applications, utilisez Mercurial.

5
Warren Dew

Une différence totalement indépendante des DVCS eux-mêmes:

Git semble être très populaire auprès des développeurs C. Git est le référentiel de facto du noyau Linux, ce qui explique peut-être sa popularité auprès des développeurs C. Ceci est particulièrement vrai pour ceux qui ont le luxe de ne travailler que dans le monde Linux/Unix.

Les développeurs Java semblent préférer Mercurial à Git. Il y a peut-être deux raisons à cela. La première est qu'un certain nombre de très grands projets Java sont hébergés sur Mercurial, y compris le JDK lui-même. La seconde est que la structure et la documentation complète de Mercurial font appel à venant du camp Java alors que de telles personnes trouvent que Git est incohérent avec un nom de commande et manque de documentation. Je ne dis pas que c'est vrai, je dis que les gens se sont habitués à quelque chose de leur habitat habituel et ensuite ils ont tendance à choisir DVCS à partir de cela.

Les développeurs Python préfèrent presque exclusivement Mercurial, je suppose. Il n’ya aucune raison rationnelle à cela, mis à part le fait que Mercurial est basé sur Python. (J'utilise aussi Mercurial et je ne comprends vraiment pas pourquoi les gens se moquent du langage d'implémentation du DVCS. Je ne comprends pas un mot de Python et si ce n'était pas pour le fait qu'il soit répertorié quelque part sur lequel il est basé Python alors je ne l'aurais pas su).

Je ne pense pas que vous puissiez dire qu'un DVCS convient mieux à une langue, vous ne devriez donc pas choisir. Mais en réalité, les gens choisissent (en partie) le DVCS auquel ils sont le plus exposés au sein de leur communauté.

(non, je n'ai pas de statistiques d'utilisation pour sauvegarder mes affirmations ci-dessus .. tout est basé sur ma propre subjectivité)

4
peterh