Skip to content

Latest commit

 

History

History
401 lines (336 loc) · 15.3 KB

File metadata and controls

401 lines (336 loc) · 15.3 KB

Git en Mercurial

Het DVCS universum is groter dan alleen Git. Eigenlijk zijn er vele andere systemen in deze ruimte, elk met hun eigen aanpak om hoe op de juiste manier om te gaan met gedistribueerd versiebeheer. Buiten Git, is de meest populaire Mercurial, en de twee zijn op vele vlakken erg vergelijkbaar.

Het goede nieuws is, als je het gedrag van Git aan de kant van het werkstation de voorkeur geeft, maar als je werkt met een project waarvan de broncode wordt beheerd met Mercurial, dat er een manier is om Git als een client voor een repository die op een Mercurial-host draait te gebruiken. Omdat de manier voor Git om te praten met server repositories via remotes loopt, moet het niet als een verrassing komen dat deze brug ("bridge") geïmplementeerd is als een remote helper. De naam van het project is git-remote-hg, en het kan worden gevonden op https://github.com/felipec/git-remote-hg.

git-remote-hg

Allereerst moet je git-remote-hg installeren. Dit houdt niet veel meer in dan het bestand ergens op je pad neer te zetten, op deze manier:

$ curl -o ~/bin/git-remote-hg \
  https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg
$ chmod +x ~/bin/git-remote-hg

…aangenomen dat ~/bin op je $PATH staat. Git-remote-hg heeft een andere afhankelijkheid: de mercurial library voor Python. Als je Python geïnstalleerd hebt, is dit zo simpel als:

$ pip install mercurial

(Als je geen Python geïnstalleerd hebt, bezoek dan https://www.python.org/ en haal dat eerst op.)

Het laatste wat je nodig hebt is de Mercurial client. Ga naar https://www.mercurial-scm.org/ en installeer dit als je dat al niet hebt gedaan.

Nu is alles klaar voor gebruik. Al wat je nodig hebt is een Mercurial repository waar je naar kunt pushen. Gelukkig kan elke Mercurial repository zich op deze manier gedragen, dus we hoeven alleen de "hello world" repository die iedereen gebruikt om Mercurial te leren gebruiken:

$ hg clone http://selenic.com/repo/hello /tmp/hello
Aan de gang gaan

Nu we een passende ``server-side'' repository hebben, kunnen we een normale workflow gaan volgen. Zoals je zult zien zijn deze twee systemen voldoende vergelijkbaar dat er niet veel wrijving zal zijn.

Zoals altijd met Git, gaan we eerst clonen:

$ git clone hg::/tmp/hello /tmp/hello-git
$ cd /tmp/hello-git
$ git log --oneline --graph --decorate
* ac7955c (HEAD, origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master, master) Create a makefile
* 65bb417 Create a standard "hello, world" program

Je zult opgemerkt hebben dat je bij het werken met een Mercurial repository het standaard git clone commando gebruikt. Dat is omdat git-remote-hg op een behoorlijk laag niveau werkt, en gebruik maakt van een vergelijkbaar mechanisme als het HTTP/S protocol dat in Git geïmplementeerd is (remote helpers). Omdat Git en Mercurial beide zijn ontworpen vanuit het principe dat elk werkstation een volledige kopie van de historie van de repository heeft, maakt dit commando een volledige kloon, inclusief alle historie van het project en doet dit redelijk snel.

Het log commando laat twee commits zien, maar naar de laatste daarvan wordt door een hele sloot refs verwezen. Nu is het zo dat een aantal van deze er eigenlijk helemaal niet zijn. Laten we kijken naar wat er eigenlijk in de .git directory staat:

$ tree .git/refs
.git/refs
├── heads
│   └── master
├── hg
│   └── origin
│       ├── bookmarks
│       │   └── master
│       └── branches
│           └── default
├── notes
│   └── hg
├── remotes
│   └── origin
│       └── HEAD
└── tags

9 directories, 5 files

Git-remote-hg probeert dingen meer letterlijk op z’n Gits te maken, maar onder de motorkap beheert het de conceptuele mapping tussen twee marginaal verschillende systemen. De refs/hg directory is waar de echte remote refs worden opgeslagen. Bijvoorbeeld, de refs/hg/origin/branche/default is een Git ref bestand die de SHA-1 bevat die begint met `ac7955c'', wat de commit is waar `master naar wijst. Dus de refs/hg directory is een soort van nep refs/remotes/origin, maar het heeft het toegevoegde onderscheid tussen boekenleggers ("bookmarks") en branches.

Het notes/hg bestand is het beginpunt van hoe git-remote-hg Git commit hashes mapt op Mercurial changeset IDs. Laten we dit een beetje verkennen:

$ cat notes/hg
d4c10386...

$ git cat-file -p d4c10386...
tree 1781c96...
author remote-hg <> 1408066400 -0800
committer remote-hg <> 1408066400 -0800

Notes for master

$ git ls-tree 1781c96...
100644 blob ac9117f...	65bb417...
100644 blob 485e178...	ac7955c...

$ git cat-file -p ac9117f
0a04b987be5ae354b710cefeba0e2d9de7ad41a9

Dus refs/notes/hg wijst naar een tree, wat in de Git object database een lijst van andere objecten met namen is. git ls-tree produceert de mode, type, object hash en bestandsnamen voor items in een tree. Zodra we gaan graven naar een van de tree items, vinden we dat hierbinnen een blog zit met de naam ac9117f'' (de SHA-1 hash van de commit waar master naar wijst), met de inhoud 0a04b98'' (wat de ID is van de Mercurial changeset aan de punt van de default-branch).

Het goede nieuwe is dat we ons over het algemeen hierover geen zorgen hoeven te maken. De typische workflow zal niet veel verschillen van het werken met een Git remote.

Er is een extra onderwerp waar we even aandacht aan moeten schenken voordat we doorgaan: ignores. Mercurial en Git gebruiken een erg vergelijkbare mechanisme hiervoor, maar het is erg aannemelijk dat je een .gitignore niet echt in een Mercurial repository wilt committen. Gelukkig heeft Git een manier om bestanden te negeren die lokaal is voor een repository die op schijf staat, en het formaat van Mercurial is compatibel met die van Git, dus je moet het ernaartoe kopiëren:

$ cp .hgignore .git/info/exclude

Het .git/info/exclude bestand gedraagt zich als een .gitignore, maar wordt niet in commits meegenomen.

Workflow

Laten we aannemen dat we wat werk gedaan hebben en een aantal commits op de master-branch uitgevoerd hebben, en je bent klaar om dit naar de remote repository te pushen. Zo ziet onze repository eruit op dit moment:

$ git log --oneline --graph --decorate
* ba04a2a (HEAD, master) Update makefile
* d25d16f Goodbye
* ac7955c (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Create a makefile
* 65bb417 Create a standard "hello, world" program

Onze master-branch loopt twee commits voor op origin/master, maar deze twee commits bestaan alleen op onze lokale machine. Laten we kijken of iemand anders belangrijk werk heeft gedaan in de tussentijd:

$ git fetch
From hg::/tmp/hello
   ac7955c..df85e87  master     -> origin/master
   ac7955c..df85e87  branches/default -> origin/branches/default
$ git log --oneline --graph --decorate --all
* 7b07969 (refs/notes/hg) Notes for default
* d4c1038 Notes for master
* df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
| * ba04a2a (HEAD, master) Update makefile
| * d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard "hello, world" program

Omdat we de --all vlag gebruikt hebben, zien we de `notes'' refs die intern gebruikt worden door git-remote-hg, maar deze kunnen we negeren. De rest is zoals we hadden verwacht; `origin/master is een commit naar voren gegaan, en onze histories zijn nu uiteengelopen. In tegenstelling tot andere systemen waar we mee werken in dit hoofdstuk, is Mercurial in staat om merges te verwerken, dus we gaan nu geen moeilijke dingen doen.

$ git merge origin/master
Auto-merging hello.c
Merge made by the 'recursive' strategy.
 hello.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph --decorate
*   0c64627 (HEAD, master) Merge remote-tracking branch 'origin/master'
|\
| * df85e87 (origin/master, origin/branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/bookmarks/master) Add some documentation
* | ba04a2a Update makefile
* | d25d16f Goodbye
|/
* ac7955c Create a makefile
* 65bb417 Create a standard "hello, world" program

Perfect. We laten de tests draaien en alles slaagt, dus we zijn klaar om ons werk te delen met de rest van het team:

$ git push
To hg::/tmp/hello
   df85e87..0c64627  master -> master

Dat is alles! Als je de Mercurial repository bekijkt, zul je zien dat dit gedaan heeft wat we mogen verwachten:

$ hg log -G --style compact
o    5[tip]:4,2   dc8fa4f932b8   2014-08-14 19:33 -0700   ben
|\     Merge remote-tracking branch 'origin/master'
| |
| o  4   64f27bcefc35   2014-08-14 19:27 -0700   ben
| |    Update makefile
| |
| o  3:1   4256fc29598f   2014-08-14 19:27 -0700   ben
| |    Goodbye
| |
@ |  2   7db0b4848b3c   2014-08-14 19:30 -0700   ben
|/     Add some documentation
|
o  1   82e55d328c8c   2005-08-26 01:21 -0700   mpm
|    Create a makefile
|
o  0   0a04b987be5a   2005-08-26 01:20 -0700   mpm
     Create a standard "hello, world" program

De wijzigingsset ("changeset") met nummer 2 is gemaakt door Mercurial, en de changesets nummers 3 en 4 zijn door git-remote-hg gemaakt, door het pushen van de met Git gemaakte commits.

Branches en Boekenleggers ("Bookmarks")

Git heeft maar een soort branch: een referentie die verplaatst wordt als commits worden gemaakt. In Mercurial, worden deze soorten referenties een ``bookmark'' genoemd, en het gedraagt zich grotendeels vergelijkbaar met een branch in Git.

Het concept van een `branch'' in Mercurial heeft iets meer voeten in de aarde. De branch waar een changeset op is gebaseerd wordt opgeslagen met de changeset, wat inhoudt dat het altijd in de historie van de repository aanwezig is. Hier is een voorbeeld van een commit die gemaakt is op de `develop-branch:

$ hg log -l 1
changeset:   6:8f65e5e02793
branch:      develop
tag:         tip
user:        Ben Straub <[email protected]>
date:        Thu Aug 14 20:06:38 2014 -0700
summary:     More documentation

Merk de regel op die begint met ``branch''. Git kan dit niet echt simuleren (en hoeft dit ook niet; beide soorten branches kunnen in Git als een ref worden weergegeven), maar git-remote-hg moet het onderscheid kunnen maken, omdat Mercurial hier om geeft.

Het aanmaken van Mercurial bookmarks is net zo eenvoudig als het maken van Git branches. Aan de Git kant:

$ git checkout -b featureA
Switched to a new branch 'featureA'
$ git push origin featureA
To hg::/tmp/hello
 * [new branch]      featureA -> featureA

En dat is alles wat nodig is. Aan de kant van Mercurial ziet het er zo uit:

$ hg bookmarks
   featureA                  5:bd5ac26f11f9
$ hg log --style compact -G
@  6[tip]   8f65e5e02793   2014-08-14 20:06 -0700   ben
|    More documentation
|
o    5[featureA]:4,2   bd5ac26f11f9   2014-08-14 20:02 -0700   ben
|\     Merge remote-tracking branch 'origin/master'
| |
| o  4   0434aaa6b91f   2014-08-14 20:01 -0700   ben
| |    update makefile
| |
| o  3:1   318914536c86   2014-08-14 20:00 -0700   ben
| |    goodbye
| |
o |  2   f098c7f45c4f   2014-08-14 20:01 -0700   ben
|/     Add some documentation
|
o  1   82e55d328c8c   2005-08-26 01:21 -0700   mpm
|    Create a makefile
|
o  0   0a04b987be5a   2005-08-26 01:20 -0700   mpm
     Create a standard "hello, world" program

Merk de nieuwe [featureA] tag in revisie 5 op. Deze gedragen zich precies als Git branches aan de Git kant, met een uitzondering: je kunt een bookmark niet van de Git kant verwijderen (dit is een beperking van remote helpers).

Je kunt ook op een `zwaargewicht'' Mercurial branch werken: gewoon een branch in de `branches naamsruimte ("namespace") zetten:

$ git checkout -b branches/permanent
Switched to a new branch 'branches/permanent'
$ vi Makefile
$ git commit -am 'A permanent change'
$ git push origin branches/permanent
To hg::/tmp/hello
 * [new branch]      branches/permanent -> branches/permanent

En hier is hoe het er aan de kant van Mercurial uit ziet:

$ hg branches
permanent                      7:a4529d07aad4
develop                        6:8f65e5e02793
default                        5:bd5ac26f11f9 (inactive)
$ hg log -G
o  changeset:   7:a4529d07aad4
|  branch:      permanent
|  tag:         tip
|  parent:      5:bd5ac26f11f9
|  user:        Ben Straub <[email protected]>
|  date:        Thu Aug 14 20:21:09 2014 -0700
|  summary:     A permanent change
|
| @  changeset:   6:8f65e5e02793
|/   branch:      develop
|    user:        Ben Straub <[email protected]>
|    date:        Thu Aug 14 20:06:38 2014 -0700
|    summary:     More documentation
|
o    changeset:   5:bd5ac26f11f9
|\   bookmark:    featureA
| |  parent:      4:0434aaa6b91f
| |  parent:      2:f098c7f45c4f
| |  user:        Ben Straub <[email protected]>
| |  date:        Thu Aug 14 20:02:21 2014 -0700
| |  summary:     Merge remote-tracking branch 'origin/master'
[...]

De branchnaam ``permanent'' is opgeslagen met de changeset gemarkeerd met 7.

Aan de kant van Git, is het werken met beide stijlen branch gelijk: gewoon checkout, commit, fetch, merge, pull en push zoals je gewoonlijk zou doen. Een ding wat je moet weten is dat Mercurial het overschrijven van historie niet ondersteunt, alleen eraan toevoegen. Dit is hoe onze Mercurial repository eruit ziet na een interactieve rebase en een force-push:

$ hg log --style compact -G
o  10[tip]   99611176cbc9   2014-08-14 20:21 -0700   ben
|    A permanent change
|
o  9   f23e12f939c3   2014-08-14 20:01 -0700   ben
|    Add some documentation
|
o  8:1   c16971d33922   2014-08-14 20:00 -0700   ben
|    goodbye
|
| o  7:5   a4529d07aad4   2014-08-14 20:21 -0700   ben
| |    A permanent change
| |
| | @  6   8f65e5e02793   2014-08-14 20:06 -0700   ben
| |/     More documentation
| |
| o    5[featureA]:4,2   bd5ac26f11f9   2014-08-14 20:02 -0700   ben
| |\     Merge remote-tracking branch 'origin/master'
| | |
| | o  4   0434aaa6b91f   2014-08-14 20:01 -0700   ben
| | |    update makefile
| | |
+---o  3:1   318914536c86   2014-08-14 20:00 -0700   ben
| |      goodbye
| |
| o  2   f098c7f45c4f   2014-08-14 20:01 -0700   ben
|/     Add some documentation
|
o  1   82e55d328c8c   2005-08-26 01:21 -0700   mpm
|    Create a makefile
|
o  0   0a04b987be5a   2005-08-26 01:20 -0700   mpm
     Create a standard "hello, world" program

Changesets 8, 9 en 10 zijn gemaakt voor en behoren bij de permanent-branch, maar de oude changesets zijn er nog steeds. Dit kan erg verwarrend worden voor je teamgenoten die Mercurial gebruiken, dus probeer dit te vermijden.

Mercurial samenvatting

Git en Mercurial zijn gelijk genoeg dat het werken over deze grenzen redelijk goed gaat. Als je het wijzigen van historie die achterblijft op je machine vermijdt (zoals over het algemeen aangeraden wordt), zou je niet eens kunnen zeggen dat Mercurial aan de andere kant staat.