Το σύμπαν των DVCS είναι μεγαλύτερο από το Git. Στην πραγματικότητα, υπάρχουν πολλά άλλα συστήματα σε αυτό το σύμπαν, το καθένα με τη δική του οπτική για το πώς γίνεται σωστά ο κατανεμημένος έλεγχος εκδόσεων. Πέρα από το Git, το πιο δημοφιλές είναι το Mercurial, και μάλιστα και δύο συστήματα είναι πολύ παρόμοια από πολλές απόψεις.
Τα καλά νέα, εφόσον προτιμάμε τη συμπεριφορά από την πλευρά του πελάτη του Git αλλά εργαζόμαστε με ένα έργο του οποίου ο πηγαίος κώδικας ελέγχεται με το Mercurial, είναι ότι υπάρχει ένας τρόπος να χρησιμοποιήσουμε το Git ως πελάτη για ένα αποθετήριο που φιλοξενείται από Mercurial.
Δεδομένου ότι ο τρόπος με τον οποίο το Git μιλάει με αποθετήρια διακομιστών μέσω απομακρυσμένων, δεν πρέπει να αποτελεί έκπληξη ότι αυτή η γέφυρα έχει υλοποιηθεί ως απομακρυσμένος βοηθός.
Το όνομα του έργου είναι git-remote-hg
και μπορεί να βρεθεί στη https://github.com/felipec/git-remote-hg.
Πρώτα, πρέπει να εγκαταστήσουμε το git-remote-hg
.
Αυτό ουσιαστικά γίνεται αν απλά αντιγράψουμε το αρχείο κάπου στη διαδρομή μας, όπως παρακάτω:
$ curl -o ~/bin/git-remote-hg \
https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg
$ chmod +x ~/bin/git-remote-hg
… με την προϋπόθεση ότι ο ~/bin
είναι στο $PATH
μας.
η git-remote-hg
έχει άλλη μία εξάρτηση: τη βιβλιοθήκη Python του Mercurial.
Αν έχουμε εγκατεστημένη την Python, αυτό γίνεται πανεύκολα:
$ pip install mercurial
(Εάν δεν έχουμε εγκαταστήσει την Python, επισκεφτόμαστε τη διεύθυνση https://www.python.org/ για να την κατεβάσουμε και να την εγκαταστήσουμε.)
Το τελευταίο πράγμα που θα χρειαστούμε είναι ο πελάτης του Mercurial. Μεταβαίνουμε στη διεύθυνση http://mercurial.selenic.com/ και τον εγκαταθιστούμε αν δεν το έχουμε ήδη κάνει.
Τώρα είμαστε έτοιμοι να παίξουμε μπάλα.
Το μόνο που χρειαζόμαστε είναι ένα αποθετήριο Mercurial στο οποίο να έχουμε δικαίωμα ώθησης.
Ευτυχώς, κάθε αποθετήριο Mercurial μπορεί να ενεργήσει με αυτόν τον τρόπο, οπότε θα χρησιμοποιήσουμε μόνο το αποθετήριο hello world
που χρησιμοποιούν όλοι για να μάθουν το Mercurial:
$ hg clone http://selenic.com/repo/hello /tmp/hello
Τώρα που έχουμε έναν κατάλληλο αποθετήριο ``από την πλευρά του διακομιστή'', μπορούμε να εξετάσουμε μια τυπική ροή εργασίας. Όπως θα δούμε, αυτά τα δύο συστήματα είναι αρκετά παρόμοια, συνεπώς δεν υπάρχουν πολλές προστριβές.
Όπως πάντα στο Git, αρχικά κλωνοποιούμε:
$ 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
Παρατηρούμε ότι η εργασία με ένα αποθετήριο Mercurial χρησιμοποιεί την τυπική εντολή git clone
.
Αυτό συμβαίνει επειδή το git-remote-hg
λειτουργεί σε αρκετά χαμηλό επίπεδο, χρησιμοποιώντας έναν παρόμοιο μηχανισμό με αυτόν που είναι υλοποιημένο το πρωτόκολλο HTTP/S του Git (με απομακρυσμένους βοηθούς).
Δεδομένου ότι τα Git και Mercurial έχουν σχεδιαστεί και τα δύο ώστε κάθε πελάτης να έχει ένα πλήρες αντίγραφο του ιστορικού του αποθετηρίου, αυτή η εντολή δημιουργεί έναν πλήρη κλώνο, συμπεριλαμβανομένου ολόκληρου του ιστορικού του έργου και μάλιστα το κάνει αρκετά γρήγορα.
Η εντολή log
δείχνει δύο υποβολές, με την πιο πρόσφατη να δείχνεται από πληθώρα αναφορών.
Αποδεικνύεται ότι κάποιες από αυτές δεν είναι πραγματικά εκεί.
Ας ρίξουμε μια ματιά στο τι υπάρχει πραγματικά στον κατάλογο .git
:
$ tree .git/refs
.git/refs
├── heads
│ └── master
├── hg
│ └── origin
│ ├── bookmarks
│ │ └── master
│ └── branches
│ └── default
├── notes
│ └── hg
├── remotes
│ └── origin
│ └── HEAD
└── tags
9 directories, 5 files
Το git-remote-h`g προσπαθεί να κάνει το ιδίωμα πιο Git-οειδές, αλλά εσωτερικά διαχειρίζεται την εννοιολογική απεικόνιση μεταξύ δύο ελαφρώς διαφορετικών συστημάτων.
Ο κατάλογος `refs/hg
είναι εκεί όπου αποθηκεύονται οι πραγματικές απομακρυσμένες αναφορές.
Για παράδειγμα, το αρχείο refs/hg/origin/branches/default
είναι ένα αρχείο ref του Git που περιέχει τον SHA-1 που αρχίζει με το ac7955c
.
Έτσι, ο κατάλογος refs/hg
είναι σαν ένα ψεύτικο refs/remotes/origin
, αλλά έχει την πρόσθετη διάκριση μεταξύ σελιδοδεικτών και κλάδων.
Το αρχείο notes/hg
είναι το σημείο εκκίνησης για τον τρόπο με τον οποίο το git-remote-hg απεικονίζει τους αριθμούς SHA-1 των υποβολών Git σε ID συνόλων αλλαγών του Mercurial.
Ας το εξερευνήσουμε αυτό λίγο:
$ 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
Επομένως, το refs/notes/hg
δείχνει σε ένα δέντρο, το οποίο στη βάση δεδομένων αντικειμένων του Git είναι μια λίστα άλλων αντικειμένων με ονόματα.
Το git ls-tree
εξάγει τα δικαιώματα πρόσβασης, τον τύπο, το hash αντικειμένου και το όνομα αρχείου για τα στοιχεία μέσα σε ένα δέντρο.
Μόλις σκάψουμε σε ένα από τα στοιχεία του δέντρου, διαπιστώνουμε ότι μέσα του υπάρχει ένα blob που ονομάζεται ac9117f
(ο αριθμός SHA-1 της υποβολής στην οποία δείχνει ο 'master`), με τα περιεχόμενα 0a04b98
(που είναι το αναγνωριστικό του συνόλου αλλαγών του Mercurial στην κορυφή του κλάδου default
.
Τα καλά νέα είναι ότι δεν χρειάζεται να ανησυχούμε για όλα αυτά. Η τυπική ροή εργασίας δεν θα είναι πολύ διαφορετική από αυτήν με ένα απομακρυσμένο αποθετήριο Git.
Υπάρχει ένα ακόμα πράγμα που πρέπει να εξετάσουμε πριν συνεχίσουμε: τα αγνοεί.
Το Mercurial και το Git χρησιμοποιούν έναν πολύ παρόμοιο μηχανισμό για αυτό, αλλά είναι πιθανό ότι δεν θέλουμε πραγματικά να υποβάλουμε ένα αρχείο .gitignore
σε ένα αποθετήριο Mercurial.
Ευτυχώς, το Git έχει έναν τρόπο να αγνοήσει αρχεία που είναι τοπικά σε ένα αποθετήριο στον δίσκο μας και η μορφή του Mercurial είναι συμβατή με το Git, οπότε απλά πρέπει να το αντιγράψουμε:
$ cp .hgignore .git/info/exclude
Το αρχείο .git/info/exclude
λειτουργεί ακριβώς όπως το .gitignore
, αλλά δεν περιλαμβάνεται στις υποβολές.
Ας υποθέσουμε ότι έχουμε κάνει κάποια εργασία και κάποιες υποβολές στον κλάδο master
και είμαστε έτοιμοι να τις ωθήσουμε στο απομακρυσμένο αποθετήριο.
Εδώ φαίνεται το αποθετήριό μας:
$ 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
Ο κλάδος master
προηγείται κατά δύο υποβολές του origin/master
, αλλά αυτές οι δύο υποβολές υπάρχουν μόνο στο τοπικό μας μηχάνημα.
Ας δούμε αν κάποιος άλλος έχει κάνει σημαντικό έργο την ίδια στιγμή:
$ 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
Εφόσον χρησιμοποιήσαμε τη σημαία --all
, βλέπουμε τις αναφορές `notes'' που χρησιμοποιούνται εσωτερικά από το `git-remote-hg
, αλλά μπορούμε να τις αγνοήσουμε.
Τα υπόλοιπα είναι όπως τα αναμέναμε· ο origin/master
έχει προχωρήσει κατά μία υποβολή και το ιστορικό μας έχει αποκλίνει τώρα.
Σε αντίθεση με τα άλλα συστήματα με τα οποία εργαζόμαστε σε αυτό το κεφάλαιο, το Mercurial είναι ικανό να χειριστεί τις συγχωνεύσεις, οπότε δεν πρόκειται να κάνουμε τίποτα φανταχτερό.
$ 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
Τέλεια. Εκτελούμε τις δοκιμές και όλα πετυχαίνουν, οπότε είμαστε έτοιμοι να μοιραστούμε το έργο μας με την υπόλοιπη ομάδα:
$ git push
To hg::/tmp/hello
df85e87..0c64627 master -> master
Αυτό ήταν! Εάν ρίξουμε μια ματιά στο αποθετήριο Mercurial, θα δούμε ότι έκανε αυτό που αναμέναμε:
$ 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
Το σύνολο των αλλαγών 2 έγινε από το Mercurial και τα σύνολα αλλαγών 3 και 4 έγιναν από το git-remote-hg
, με ώθηση των υποβολών που έγιναν με το Git.
Το Git έχει μόνο ένα είδος κλάδου: μια αναφορά που μετακινείται όταν γίνονται υποβολές. Στο Mercurial, αυτό το είδος μίας αναφοράς ονομάζεται ``σελιδοδείκτης'' και συμπεριφέρεται με τον ίδιο τρόπο όπως ένας κλάδος Git.
Η αντίληψη του Mercurial περί `κλάδου'' είναι πιο βαριά.
Ο κλάδος στο οποίο πραγματοποιείται ένα σύνολο αλλαγών καταγράφεται με το σύνολο αλλαγών (changeset), που σημαίνει ότι θα βρίσκεται πάντα στο ιστορικό του αποθετηρίου.
Ακολουθεί ένα παράδειγμα υποβολής που έγινε στον κλάδο `develop
:
$ hg log -l 1
changeset: 6:8f65e5e02793
branch: develop
tag: tip
user: Ben Straub <[email protected]>
date: Thu Aug 14 20:06:38 2014 -0700
summary: More documentation
Παρατηρούμε τη γραμμή που αρχίζει με branch
.
Το Git δεν μπορεί να αναπαράγει αυτό το πράγμα (και ούτε χρειάζεται, αφού και οι δύο τύποι κλάδων μπορούν να αναπαρασταθούν ως ref του Git), αλλά το git-remote-hg
πρέπει να κατανοήσει τη διαφορά, επειδή το Mercurial πρέπει να ξέρει.
Η δημιουργία των σελιδοδεικτών στο Mercurial είναι τόσο εύκολη όσο και η δημιουργία κλάδων στο Git. Από την πλευρά του Git:
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ git push origin featureA
To hg::/tmp/hello
* [new branch] featureA -> featureA
Αυτό ήταν όλο. Από την πλευρά του Mercurial, μοιάζει με αυτό:
$ 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
Παρατηρούμε τη νέα ετικέτα [featureA]
στην αναθεώρηση 5.
Αυτά λειτουργούν ακριβώς όπως οι κλάδοι Git στην πλευρά του Git, με μια εξαίρεση: δεν μπορούμε να διαγράψουμε έναν σελιδοδείκτη από την πλευρά του Git (αυτός είναι ένας περιορισμός των απομακρυσμένων βοηθών).
Επίσης μπορούμε να εργαστούμε σε έναν κλάδο `βαρέων βαρών'' του Mercurial: απλά βάζουμε έναν κλάδο στον ονοματοχώρο `branches
:
$ 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
Ακολουθεί το πώς μοιάζει από την πλευρά του Mercurial:
$ hg branches
permanent 7:a4529d07aad4
develop 6:8f65e5e02793
default 5:bd5ac26f11f9 (inactive)
$ hg log -G
o changeset: 7:a4529d07aad4
| branch: permanent
| tag: tip
| parent: 5:bd5ac26f11f9
| user: Ben Straub <[email protected]>
| date: Thu Aug 14 20:21:09 2014 -0700
| summary: A permanent change
|
| @ changeset: 6:8f65e5e02793
|/ branch: develop
| user: Ben Straub <[email protected]>
| date: Thu Aug 14 20:06:38 2014 -0700
| summary: More documentation
|
o changeset: 5:bd5ac26f11f9
|\ bookmark: featureA
| | parent: 4:0434aaa6b91f
| | parent: 2:f098c7f45c4f
| | user: Ben Straub <[email protected]>
| | date: Thu Aug 14 20:02:21 2014 -0700
| | summary: Merge remote-tracking branch 'origin/master'
[...]
Το όνομα κλάδου permanent
καταγράφηκε με το σύνολο αλλαγών 7.
Από την πλευρά του Git, η συνεργασία με οποιοδήποτε από αυτά τα στυλ κλάδου είναι ίδια: απλώς ελέγχουμε, διεκπεραιώνουμε, ανακτούμε, συγχωνεύουμε, έλκουμε και ωθούμε όπως θα κάναμε κανονικά. Ένα πράγμα που πρέπει να ξέρουμε είναι ότι το Mercurial δεν υποστηρίζει την επανεγγραφή ιστορικού και μόνο προσθέτει σε αυτό. Ακολουθεί η εμφάνιση του αποθετηρίου μας Mercurial μετά από μια διαδραστική αλλαγή βάσης και μια εξαναγκασμένη ώθηση:
$ 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
Οι αλλαγές 8, 9 και 10 έχουν δημιουργηθεί και ανήκουν στον κλάδο permanent
, αλλά τα παλιά σύνολα αλλαγών είναι ακόμα εκεί.
Αυτό μπορεί να επιφέρει πολλή σύγχυση στους συνεργάτες μας που χρησιμοποιούν Mercurial, οπότε προσπαθούμε να το αποφύγουμε.