-
1. Ξεκινώντας με το Git
-
2. Τα θεμελιώδη στοιχεία του Git
-
3. Διακλαδώσεις στο Git
-
4. Το Git στον διακομιστή
- 4.1 Τα πρωτόκολλα
- 4.2 Εγκατάσταση του Git σε διακομιστή
- 4.3 Δημιουργία δημόσιου κλειδιού SSH
- 4.4 Στήσιμο του διακομιστή
- 4.5 Δαίμονες του Git
- 4.6 Έξυπνο HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Επιλογές φιλοξενίας από τρίτους
- 4.10 Ανακεφαλαίωση
-
5. Κατανεμημένο Git
-
6. GitHub
-
7. Εργαλεία του Git
- 7.1 Επιλογή αναθεώρησης
- 7.2 Διαδραστική εργασία με το στάδιο καταχώρισης
- 7.3 stash και clean
- 7.4 Υπογραφή της δουλειάς μας
- 7.5 Αναζήτηση
- 7.6 Η ιστορία ξαναγράφεται
- 7.7 Απομυθοποίηση της reset
- 7.8 Συγχωνεύσεις για προχωρημένους
- 7.9 Rerere
- 7.10 Αποσφαλμάτωση με το Git
- 7.11 Λειτουργικές υπομονάδες
- 7.12 Δεμάτιασμα δεδομένων
- 7.13 Replace
- 7.14 Αποθήκευση διαπιστευτηρίων
- 7.15 Ανακεφαλαίωση
-
8. Εξατομίκευση του Git
-
9. Το Git και άλλα συστήματα
- 9.1 Το Git ως πελάτης
- 9.2 Μετανάστευση στο Git
- 9.3 Ανακεφαλαίωση
-
10. Εσωτερική λειτουργία του Git
- 10.1 Διοχετεύσεις και πορσελάνες
- 10.2 Αντικείμενα του Git
- 10.3 Αναφορές του Git
- 10.4 Πακετάρισμα αρχείων
- 10.5 Τα refspec
- 10.6 Πρωτόκολλα μεταφοράς
- 10.7 Διατήρηση και ανάκτηση δεδομένων
- 10.8 Μεταβλητές περιβάλλοντος
- 10.9 Ανακεφαλαίωση
-
A1. Appendix A: Το Git σε άλλα περιβάλλοντα
- A1.1 Γραφικές διεπαφές
- A1.2 Το Git στο Visual Studio
- A1.3 Git στο Eclipse
- A1.4 Το Git στο Bash
- A1.5 Το Git στο Zsh
- A1.6 Το Git στο Powershell
- A1.7 Ανακεφαλαίωση
-
A2. Appendix B: Ενσωμάτωση του Git στις εφαρμογές μας
- A2.1 Γραμμή εντολών Git
- A2.2 Libgit2
- A2.3 JGit
-
A3. Appendix C: Εντολές Git
- A3.1 Ρύθμιση και διαμόρφωση
- A3.2 Λήψη και δημιουργία έργων
- A3.3 Βασική λήψη στιγμιοτύπων
- A3.4 Διακλάδωση και συγχώνευση
- A3.5 Κοινή χρήση και ενημέρωση έργων
- A3.6 Επιθεώρηση και σύγκριση
- A3.7 Αποσφαλμάτωση
- A3.8 Επιθέματα
- A3.9 Ηλεκτρονικό ταχυδρομείο
- A3.10 Εξωτερικά Συστήματα
- A3.11 Διοίκηση
- A3.12 Εντολές διοχέτευσης
9.1 Το Git και άλλα συστήματα - Το Git ως πελάτης
Ο κόσμος δεν είναι τέλειος. Συνήθως, δεν μπορούμε να μεταφέρουμε άμεσα κάθε έργο με το οποίο ερχόμαστε σε επαφή στο Git. Μερικές φορές είμαστε κολλημένοι σε ένα έργο που χρησιμοποιεί ένα άλλο VCS και επιθυμούμε να είναι το Git. Θα αφιερώσουμε το πρώτο μέρος αυτού του κεφαλαίου για να μάθουμε τους τρόπους χρήσης του Git ως πελάτη όταν το έργο στο οποίο εργαζόμαστε φιλοξενείται σε διαφορετικό σύστημα.
Σε κάποιο σημείο, μπορεί να θέλουμε να μετατρέψουμε το υπάρχον έργο μας στο Git. Το δεύτερο μέρος αυτού του κεφαλαίου καλύπτει τον τρόπο με τον οποίο μπορούμε να μεταφέρουμε το έργο μας σε Git από διάφορα συγκεκριμένα συστήματα, καθώς και μια μέθοδο που λειτουργεί ακόμα κι εάν δεν υπάρχει κάποιο έτοιμοι εργαλείο εισαγωγής.
Το Git ως πελάτης
Το Git προσφέρει μια τόσο όμορφη εμπειρία για τους προγραμματιστές που πολλοί έχουν βρει έναν τρόπο να το χρησιμοποιούν στον σταθμό εργασίας τους ακόμα κι αν η υπόλοιπη ομάδα τους χρησιμοποιεί ένα εντελώς διαφορετικό VCS. Τέτοιου είδους προσαρμογείς, ονομάζονται “γέφυρες” και υπάρχουν μερικοί διαθέσιμοι. Εδώ θα καλύψουμε εκείνους που είναι το πιθανότερο να συναντήσουμε στον έξω κόσμο.
Git and Subversion
Ένα μεγάλο μέρος των έργων ανάπτυξης ανοιχτού κώδικα και ένας μεγάλος αριθμός εταιρικών έργων χρησιμοποιεί το Subversion για τη διαχείριση του πηγαίου κώδικα. Το Subversion υπάρχει εδώ και περισσότερα από 10 χρόνια και για το μεγαλύτερο μέρος της εποχής εκείνης υπήρξε η de facto επιλογή ενός VCS για έργα ανοιχτού κώδικα. Είναι επίσης πολύ παρόμοια με πολλούς τρόπους με το CVS, το οποίο ήταν ο μεγάλος παίκτης του ελέγχου πηγαίου κώδικα πριν από αυτό.
Ένα από τα εξαιρετικά χαρακτηριστικά του Git είναι μια αμφίδρομη γέφυρα με το Subversion που ονομάζεται git svn
.
Αυτό το εργαλείο μάς επιτρέπει να χρησιμοποιούμε το Git ως έγκυρο πελάτη σε έναν διακομιστή Subversion, ώστε να μπορούμε να χρησιμοποιήσουμε όλες τις τοπικές λειτουργίες του Git και στη συνέχεια να ωθήσουμε σε έναν διακομιστή Subversion σαν να χρησιμοποιούσαμε το Subversion τοπικά.
Αυτό σημαίνει ότι μπορούμε να κάνουμε τοπικά διακλάδωση και συγχώνευση, να χρησιμοποιήσουμε το στάδιο καταχώρισης, να χρησιμοποιήσουμε την αλλαγή βάσης και την ανθολόγηση κ.ο.κ., ενώ οι συνεργάτες μας συνεχίζουν να εργάζονται με τους σκοτεινούς και αρχαίους τρόπους τους.
Είναι ένας καλός τρόπος να περάσουμε αθόρυβα το Git στο εταιρικό περιβάλλον και να βοηθήσουμε τους συνεργάτες μας να γίνουν πιο αποδοτικοί ενώ εμείς ασχολούμαστε να ασκούμε παρασκηνιακές πιέσεις ώστε να αλλάξουμε τις υποδομές ώστε να υποστηρίζουν το Git πλήρως.
Η γέφυρα Subversion είναι το ναρκωτικό-προθάλαμος στον κόσμο του DVCS.
git svn
Η βασική εντολή στο Git για όλες τις εντολές γεφύρωσης με το Subversion είναι η git svn
.
Χρειάζονται αρκετές υποεντολές, επομένως θα δείξουμε τις πιο κοινές ενώ βλέπουμε και μερικές απλές ροές εργασίας.
Είναι σημαντικό να σημειώσουμε ότι όταν χρησιμοποιούμε την git svn
, αλληλεπιδράμε με το Subversion, το οποίο είναι ένα σύστημα που λειτουργεί πολύ διαφορετικά από το Git.
Παρόλο που μπορούμε να κάνουμε τοπικά διακλαδώσεις και συγχωνεύσεις, είναι γενικά καλύτερα να διατηρήσουμε το ιστορικό μας κατά το δυνατόν γραμμικό, αλλάζοντας τη βάση του έργου μας και αποφεύγοντας να κάνουμε πράγματα όπως να αλληλεπιδρούμε ταυτόχρονα με ένα απομακρυσμένο αποθετήριο Git.
Δεν πρέπει να ξαναγράφουμε το ιστορικό μας και μετά να ωθούμε, ούτε να ωθούμε παράλληλα σε κάποιο αποθετήριο Git για να συνεργαζόμαστε με συνεργάτες προγραμματιστές σε Git. Το Subversion μπορεί να έχει μόνον ένα γραμμικό ιστορικό και το να το μπερδέψουμε είναι πολύ εύκολο. Αν εργαζόμαστε μέσα σε μια ομάδα και κάποιοι χρησιμοποιούν το SVN και άλλοι χρησιμοποιούν το Git, πρέπει να βεβαιωθούμε ότι όλοι χρησιμοποιούν τον διακομιστή SVN για να συνεργαστούν —αυτό θα κάνει τη ζωή μας ευκολότερη.
Εγκατάσταση
Για να επιδείξουμε αυτήν τη λειτουργικότητα, χρειαζόμαστε ένα τυπικό αποθετήριο SVN στο οποίο έχουμε πρόσβαση εγγραφής.
Για να αντιγράψουμε αυτά τα παραδείγματα, θα πρέπει να δημιουργήσουμε ένα αντίγραφο της δοκιμαστικής αποθήκης.
Για να γίνει αυτό εύκολα, μπορούμε να χρησιμοποιήσουμε ένα εργαλείο που ονομάζεται svnsync
που συνοδεύει το Subversion.
Για αυτές τις δοκιμές δημιουργήσαμε ένα νέο αποθετήριο Subversion στο Google Code το οποίο ήταν ένα μερικό αντίγραφο του έργου protobuf
, το οποίο είναι ένα εργαλείο που κωδικοποιεί δομημένα δεδομένα για μετάδοση στο δίκτυο.
Πρέπει πρώτα να δημιουργήσουμε ένα νέο τοπικό αποθετήριο Subversion:
$ mkdir /tmp/test-svn
$ svnadmin create /tmp/test-svn
Στη συνέχεια, επιτρέπουμε σε όλους τους χρήστες να αλλάξουν revprops —ο εύκολος τρόπος είναι να προσθέσουμε ένα σενάριο pre-revprop-change
που πάντα τερματίζει με 0:
$ cat /tmp/test-svn/hooks/pre-revprop-change
#!/bin/sh
exit 0;
$ chmod +x /tmp/test-svn/hooks/pre-revprop-change
Τώρα μπορούμε να συγχρονίσουμε αυτό το έργο με το τοπικό μας μηχάνημα καλώντας την svnsync init
με τα αποθετήρια από και προς.
$ svnsync init file:///tmp/test-svn \
http://progit-example.googlecode.com/svn/
Αυτό ρυθμίζει τις ιδιότητες για την εκτέλεση του συγχρονισμού. Στη συνέχεια μπορούμε να κλωνοποιήσουμε τον κώδικα εκτελώντας:
$ svnsync sync file:///tmp/test-svn
Committed revision 1.
Copied properties for revision 1.
Transmitting file data .............................[...]
Committed revision 2.
Copied properties for revision 2.
[…]
Παρόλο που η λειτουργία αυτή μπορεί να διαρκέσει μόνο λίγα λεπτά, αν προσπαθήσουμε να αντιγράψουμε τον αρχικό αποθετήριο σε ένα άλλο απομακρυσμένο αντί για ένα τοπικό αποθετήριο, η διαδικασία θα διαρκέσει περίπου μία ώρα, παρότι υπάρχουν λιγότερες από 100 υποβολές. Το Subversion πρέπει να κλωνοποιεί μια αναθεώρηση κάθε φορά και στη συνέχεια να την ωθεί ξανά σε ένα άλλο αποθετήριο -είναι γελοία αναποτελεσματικό, αλλά είναι ο μόνος εύκολος τρόπος για να το κάνουμε.
Ξεκίνημα
Τώρα που έχουμε ένα αποθετήριο Subversion στο οποίο έχουμε πρόσβαση εγγραφής, μπορούμε να περάσουμε από μια τυπική ροή εργασίας.
Θα ξεκινήσουμε με την εντολή git svn clone
, η οποία εισάγει ένα ολόκληρο αποθετήριο Subversion σε ένα τοπικό αποθετήριο Git.
Ας θυμηθούμε ότι αν εισάγουμε από ένα πραγματικό αποθετήριο Subversion, θα πρέπει να αντικαταστήσουμε εδώ την file:///tmp/test-svn
που υπάρχει εδώ με τη διεύθυνση URL του αποθετηρίου Subversion:
$ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags
Initialized empty Git repository in /private/tmp/progit/test-svn/.git/
r1 = dcbfb5891860124cc2e8cc616cded42624897125 (refs/remotes/origin/trunk)
A m4/acx_pthread.m4
A m4/stl_hash.m4
A java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
A java/src/test/java/com/google/protobuf/WireFormatTest.java
…
r75 = 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae (refs/remotes/origin/trunk)
Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/my-calc-branch, 75
Found branch parent: (refs/remotes/origin/my-calc-branch) 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae
Following parent with do_switch
Successfully followed parent
r76 = 0fb585761df569eaecd8146c71e58d70147460a2 (refs/remotes/origin/my-calc-branch)
Checked out HEAD:
file:///tmp/test-svn/trunk r75
Η παραπάνω εκτελεί το ισοδύναμο δύο εντολών —git svn init
ακολουθούμενη από την git svn fetch
— στη διεύθυνση URL που παρέχουμε.
Αυτό ίσως διαρκέσει λίγο.
Το δοκιμαστικό έργο έχει μόνο περίπου 75 υποβολές και η βάση του κώδικα δεν είναι τόσο μεγάλη, αλλά το Git πρέπει να ελέγχει κάθε έκδοση μία μία και να την υποβάλει ξεχωριστά.
Για ένα έργο με εκατοντάδες ή χιλιάδες υποβολές, αυτό μπορεί να πάρει κυριολεκτικά ώρες ή ακόμα και ημέρες για να ολοκληρωθεί.
Το τμήμα
The -T trunk -b branches -t tags
λέει στο Git ότι αυτό το αποθετήριο Subversion ακολουθεί τις βασικές συμβάσεις διακλάδωσης και σήμανσης με ετικέτες.
Εάν ονομάσουμε τον κορμό, τους κλάδους ή τις ετικέτες διαφορετικά, μπορούμε να αλλάξουμε αυτές τις επιλογές.
Επειδή αυτό είναι τόσο συνηθισμένο, μπορούμε να αντικαταστήσουμε ολόκληρο αυτό το τμήμα με -s
, που σημαίνει τυποποιημένη (standard) διάταξη και υπονοεί όλες αυτές τις επιλογές.
Η ακόλουθη εντολή είναι ισοδύναμη:
$ git svn clone file:///tmp/test-svn -s
Σε αυτό το σημείο, θα πρέπει να έχουμε ένα έγκυρο αποθετήριο Git που έχει εισάγει τους κλάδους και τις ετικέτες μας:
$ git branch -a
* master
remotes/origin/my-calc-branch
remotes/origin/tags/2.0.2
remotes/origin/tags/release-2.0.1
remotes/origin/tags/release-2.0.2
remotes/origin/tags/release-2.0.2rc1
remotes/origin/trunk
Ας σημειωθεί πώς το εργαλείο διαχειρίζεται τις ετικέτες Subversion ως απομακρυσμένες ref.
Ας ρίξουμε μια πιο προσεκτική ματιά στην εντολή διοχέτευσης show-ref
του Git :
$ git show-ref
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/heads/master
0fb585761df569eaecd8146c71e58d70147460a2 refs/remotes/origin/my-calc-branch
bfd2d79303166789fc73af4046651a4b35c12f0b refs/remotes/origin/tags/2.0.2
285c2b2e36e467dd4d91c8e3c0c0e1750b3fe8ca refs/remotes/origin/tags/release-2.0.1
cbda99cb45d9abcb9793db1d4f70ae562a969f1e refs/remotes/origin/tags/release-2.0.2
a9f074aa89e826d6f9d30808ce5ae3ffe711feda refs/remotes/origin/tags/release-2.0.2rc1
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/remotes/origin/trunk
Το Git δεν το κάνει αυτό όταν κλωνοποιεί από έναν διακομιστή Git· παρακάτω φαίνεται με τι μοιάζει ένα αποθετήριο με ετικέτες αμριβώς μετά την κλωνοποίηση:
$ git show-ref
c3dcbe8488c6240392e8a5d7553bbffcb0f94ef0 refs/remotes/origin/master
32ef1d1c7cc8c603ab78416262cc421b80a8c2df refs/remotes/origin/branch-1
75f703a3580a9b81ead89fe1138e6da858c5ba18 refs/remotes/origin/branch-2
23f8588dde934e8f33c263c6d8359b2ae095f863 refs/tags/v0.1.0
7064938bd5e7ef47bfd79a685a62c1e2649e2ce7 refs/tags/v0.2.0
6dcb09b5b57875f334f61aebed695e2e4193db5e refs/tags/v1.0.0
Το Git ανακτά τις ετικέτες κατευθείαν στο φάκελο refs/tags
, αντί να τις αντιμετωπίζει ως απομακρυσμένους κλάδους.
Υποβολή στο Subversion
Τώρα που έχουμε ένα αποθετήριο στο οποίο εργαζόμαστε, μπορούμε να κάνουμε κάποια εργασία στο έργο και να ωθήσουμε τις υποβολές μας προς τα πάνω, ουσιαστικά χρησιμοποιώντας το Git ως πελάτη SVN. Εάν επεξεργαστούμε ένα από τα αρχεία και το υποβάλουμε, έχουμε μια υποβολή που υπάρχει στο Git τοπικά και δεν υπάρχει στον διακομιστή Subversion:
$ git commit -am 'Adding git-svn instructions to the README'
[master 4af61fd] Adding git-svn instructions to the README
1 file changed, 5 insertions(+)
Στη συνέχεια, πρέπει να ωθήσουμε την αλλαγή μας προς τα πάνω.
Αυτό αλλάζει τον τρόπο με τον οποίο εργαζόμαστε με το Subversion —μπορούμε να εκτελέσουμε αρκετές υποβολές τοπικά και στη συνέχεια να τις ωθήσουμε ταυτόχρονα στον διακομιστή Subversion.
Για να ωθήσουμε σε έναν διακομιστή Subversion, εκτελούμε την εντολή git svn dcommit
:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M README.txt
Committed r77
M README.txt
r77 = 95e0222ba6399739834380eb10afcd73e0670bc5 (refs/remotes/origin/trunk)
No changes between 4af61fd05045e07598c553167e0f31c84fd6ffe1 and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
Αυτό λαμβάνει όλες τις υποβολές που κάναμε πάνω από τον κώδικα του διακομιστή Subversion, πραγματοποιεί μια υποβολή Subversion για καθεμία και μετά ξαναγράφει την τοπική μας υποβολή Git για να συμπεριλάβουμε ένα μοναδικό αναγνωριστικό.
Αυτό είναι σημαντικό επειδή σημαίνει ότι όλα τα αθροίσματα ελέγχου SHA-1 για τις υποβολές μας αλλάζουν.
Εν μέρει για αυτόν τον λόγο, η εργασία με απομακρυσμένες εκδόσεις του έργου μας σε Git ταυτόχρονα με εργασία σε έναν διακομιστή Subversion δεν είναι καλή ιδέα.
Αν κοιτάξουμε την τελευταία υποβολή, μπορούμε να δούμε το νέο git-svn-id
που προστέθηκε:
$ git log -1
commit 95e0222ba6399739834380eb10afcd73e0670bc5
Author: ben <ben@0b684db3-b064-4277-89d1-21af03df0a68>
Date: Thu Jul 24 03:08:36 2014 +0000
Adding git-svn instructions to the README
git-svn-id: file:///tmp/test-svn/trunk@77 0b684db3-b064-4277-89d1-21af03df0a68
Παρατηρούμε ότι το άθροισμα ελέγχου SHA-1 που ξεκίνησε αρχικά με το 4af61fd
όταν υποβάλαμε τώρα αρχίζει με` 95e0222`.
Αν θέλουμε να ωθήσουμε τόσο σε έναν διακομιστή Git όσο και σε έναν διακομιστή Subversion, πρέπει πρώτα να ωθήσουμε (dcommit
) στον διακομιστή Subversion, επειδή η ενέργεια αυτή αλλάζει τα δεδομένα της υποβολής μας.
Έλξη νέων αλλαγών
Εάν συνεργαζόμαστε με άλλους προγραμματιστές, τότε σε κάποιο σημείο ένας από εμάς θα ωθήσει και μετά κάποιος άλλος θα προσπαθήσει να ωθήσει μια αλλαγή που έρχεται σε σύγκρουση με την πρώτη.
Η αλλαγή αυτή θα απορριφθεί μέχρι να συγχωνεύσουμε την εργασία του.
Στην git svn
, μοιάζει με αυτό:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
ERROR from SVN:
Transaction is out of date: File '/trunk/README.txt' is out of date
W: d5837c4b461b7c0e018b49d12398769d2bfc240a and refs/remotes/origin/trunk differ, using rebase:
:100644 100644 f414c433af0fd6734428cf9d2a9fd8ba00ada145 c80b6127dd04f5fcda218730ddf3a2da4eb39138 M README.txt
Current branch master is up to date.
ERROR: Not all changes have been committed into SVN, however the committed
ones (if any) seem to be successfully integrated into the working tree.
Please see the above messages for details.
Για να επιλύσουμε αυτήν την κατάσταση, μπορούμε να εκτελέσουμε την git svn rebase
, η οποία καταργεί οποιεσδήποτε αλλαγές στον διακομιστή δεν έχουμε ακόμα και επαντοποθετεί οποιαδήποτε εργασία έχουμε πάνω από αυτό που βρίσκεται στον server:
$ git svn rebase
Committing to file:///tmp/test-svn/trunk ...
ERROR from SVN:
Transaction is out of date: File '/trunk/README.txt' is out of date
W: eaa029d99f87c5c822c5c29039d19111ff32ef46 and refs/remotes/origin/trunk differ, using rebase:
:100644 100644 65536c6e30d263495c17d781962cfff12422693a b34372b25ccf4945fe5658fa381b075045e7702a M README.txt
First, rewinding head to replay your work on top of it...
Applying: update foo
Using index info to reconstruct a base tree...
M README.txt
Falling back to patching base and 3-way merge...
Auto-merging README.txt
ERROR: Not all changes have been committed into SVN, however the committed
ones (if any) seem to be successfully integrated into the working tree.
Please see the above messages for details.
Τώρα, όλη η δουλειά μας είναι πάνω από αυτό που βρίσκεται στον διακομιστή Subversion, έτσι ώστε να μπορούμε να τρέξουμε dcommit
με επιτυχία:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M README.txt
Committed r85
M README.txt
r85 = 9c29704cc0bbbed7bd58160cfb66cb9191835cd8 (refs/remotes/origin/trunk)
No changes between 5762f56732a958d6cfda681b661d2a239cc53ef5 and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
Ας σημειωθεί ότι αντίθετα με το Git, το οποίο απαιτεί να συγχωνεύσουμε την εργασία που έχει γίνει upstream και δεν έχουμε ακόμα τοπικά προτού μπορέσουμε να ωθήσουμε, το git svn
μας επιβάλει να το κάνουμε αυτό μόνο αν οι αλλαγές συγκρούονται (όπως ακριβώς λειτουργεί το Subversion).
Αν κάποιος άλλος ωθήσει μια αλλαγή σε ένα αρχείο και στη συνέχεια ωθήσουμε μια αλλαγή σε άλλο αρχείο, το dcommit
μας θα λειτουργήσει μια χαρά:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M configure.ac
Committed r87
M autogen.sh
r86 = d8450bab8a77228a644b7dc0e95977ffc61adff7 (refs/remotes/origin/trunk)
M configure.ac
r87 = f3653ea40cb4e26b6281cec102e35dcba1fe17c4 (refs/remotes/origin/trunk)
W: a0253d06732169107aa020390d9fefd2b1d92806 and refs/remotes/origin/trunk differ, using rebase:
:100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 e757b59a9439312d80d5d43bb65d4a7d0389ed6d M autogen.sh
First, rewinding head to replay your work on top of it...
Αυτό είναι σημαντικό να το θυμόμαστε, επειδή το αποτέλεσμα είναι μια κατάσταση του έργου που δεν υπήρχε σε κανέναν από τους υπολογιστές μας όταν ώθησαμε. Εάν οι αλλαγές είναι ασυμβίβαστες αλλά δεν έρχονται σε σύγκρουση, μπορεί να έχουμε προβλήματα που είναι δύσκολο να διαγνωστούν. Αυτό είναι διαφορετικό από όταν χρησιμοποιούμε διακομιστή Git —στο Git μπορούμε να ελέγξουμε πλήρως την κατάσταση του πελάτη μας πριν δημοσιεύσουμε, ενώ στο SVN, δεν μπορούμε ποτέ να είμαστε σίγουροι ότι οι καταστάσεις αμέσως πριν και αμέσως μετά την υποβολή είναι πανομοιότυπα.
Θα πρέπει επίσης να εκτελέσουμε αυτήν την εντολή για να έλξουμε αλλαγές από τον διακομιστή Subversion, ακόμη και αν δεν είμαστε έτοιμοι να υποβάλουμε.
Μπορούμε να εκτελέσουμε το git svn fetch
για να τραβήξουμε τα νέα δεδομένα, αλλά η git svn rebase
κάνει την ανάκτηση και στη συνέχεια ενημερώνει τις τοπικές υποβολές μας.
$ git svn rebase
M autogen.sh
r88 = c9c5f83c64bd755368784b444bc7a0216cc1e17b (refs/remotes/origin/trunk)
First, rewinding head to replay your work on top of it...
Fast-forwarded master to refs/remotes/origin/trunk.
Το τρέξιμο της git svn rebase
κάθε τόσο διασφαλίζει ότι ο κώδικάς μας είναι πάντα ενημερωμένος.
Όμως θα πρέπει να είμαστε σίγουροι ότι ο κατάλογος εργασίας μας είναι καθαρός όταν την εκτελούμε.
Αν έχουμε τοπικές αλλαγές, πρέπει είτε να φυλάξουμε την εργασία μας σε κάποιο απόθεμα είτε να την υποβάλουμε προσωρινά πριν εκτελέσουμε την git svn rebase
—διαφορετικά, η εντολή θα σταματήσει εάν διαπιστώσει ότι η αλλαγή βάσης θα οδηγήσει σε σύγκρουση συγχώνευσης.
Ζητήματα διακλάδωσης στο Git
Όταν εξοικειωθούμε με μια ροή εργασίας του Git, πιθανότατα θα δημιουργήσουμε θεματικούς κλάδους, θα εργαστούμε σε αυτούς και στη συνέχεια θα τους συγχωνεύσουμε.
Αν ωθούμε σε έναν διακομιστή Subversion μέσω της git svn
, ίσως θελήσουμε να αλλάξουμε τη βάση της εργασίας μας σε έναν κλάδο κάθε φορά αντί να συγχωνεύσουμε τους κλάδους.
Ο λόγος για τον οποίο προτιμάμε την αλλαγή της βάσης είναι ότι το Subversion έχει γραμμικό ιστορικό και δεν αντιμετωπίζει τις συγχωνεύσεις όπως το Git, έτσι η git svn
ακολουθεί μόνο τον πρώτο γονέα όταν μετατρέπει τα στιγμιότυπα σε υποβολές Subversion.
Ας υποθέσουμε ότι το ιστορικό μας μοιάζει με το ακόλουθο: δημιουργήσαμε έναν κλάδο experiment
, κάναμε δύο υποβολές και στη συνέχεια τους συγχωνεύσαμε ξανά στον master
.
Όταν κάνουμε dcommit
, βλέπουμε την εξής έξοδο:
$ git svn dcommit
Committing to file:///tmp/test-svn/trunk ...
M CHANGES.txt
Committed r89
M CHANGES.txt
r89 = 89d492c884ea7c834353563d5d913c6adf933981 (refs/remotes/origin/trunk)
M COPYING.txt
M INSTALL.txt
Committed r90
M INSTALL.txt
M COPYING.txt
r90 = cb522197870e61467473391799148f6721bcf9a0 (refs/remotes/origin/trunk)
No changes between 71af502c214ba13123992338569f4669877f55fd and refs/remotes/origin/trunk
Resetting to the latest refs/remotes/origin/trunk
Η εκτέλεση της dcommit
σε έναν κλάδο με συγχωνευμένο ιστορικό λειτουργεί καλά, εκτός από το ότι όταν εξετάζουμε το ιστορικό του έργου Git, δεν έχει ξαναγράψει καμία από τις υποβολές που κάναμε στον κλάδο experiment
—αντίθετα όλες αυτές οι αλλαγές εμφανίζονται στην έκδοση SVN της μοναδικής υποβολής συγχώνευσης.
Όταν κάποιος άλλος κλωνοποιεί αυτό το έργο, το μόνο που βλέπει είναι ότι η συγχώνευση υποβάλλεται με όλη τη δουλειά που στριμώχνεται σε αυτό, σαν να είχαμε τρέξει git merge --squash
· δεν βλέπουν τα δεδομένα υποβολής σχετικά με το από πού προήλθε ή πότε υποβλήθηκε.
Διακλαδώσεις στο Subversion
Η διακλαδώσεις στο Subversion δεν είναι ίδιες με τις διακλαδώσεις στο Git· αν μπορούμε να αποφύγουμε να τις χρησιμοποιούμε πολύ, αυτό είναι μάλλον το καλύτερο.
Ωστόσο, μπορούμε να δημιουργήσουμε κλάδους και τους υποβάλουμε στο Subversion χρησιμοποιώντας την git svn
.
Δημιουργία νέου κλάδου στο SVN
Για να δημιουργήσουμε έναν νέο κλάδο στο Subversion, εκτελούμε την git svn branch [όνομα_κλάδου]
:
$ git svn branch opera
Copying file:///tmp/test-svn/trunk at r90 to file:///tmp/test-svn/branches/opera...
Found possible branch point: file:///tmp/test-svn/trunk => file:///tmp/test-svn/branches/opera, 90
Found branch parent: (refs/remotes/origin/opera) cb522197870e61467473391799148f6721bcf9a0
Following parent with do_switch
Successfully followed parent
r91 = f1b64a3855d3c8dd84ee0ef10fa89d27f1584302 (refs/remotes/origin/opera)
Αυτό ισοδυναμεί με την εντολή svn copy trunk branches/opera
στο Subversion και λειτουργεί στον διακομιστή Subversion.
Είναι σημαντικό να σημειωθεί ότι αυτή η εντολή δεν μας μεταφέρει σε αυτόν τον κλάδο· εάν υποβάλουμε σε αυτό το σημείο, αυτή η υποβολή θα μεταβεί στον trunk
του διακομιστή, όχι στον όπερα
.
Μετάβαση μεταξύ ενεργών κλάδων
Το Git υπολογίζει σε ποιον κλάδο πηγαίνουν τα dcommit μας αναζητώντας την άκρη όλων των κλάδων Subversion στο ιστορικό μας —θα πρέπει να έχουμε μόνο ένα και θα πρέπει να είναι το τελευταίο με ένα git-svn-id
στο τρέχον ιστορικό του κλάδου μας.
Εάν θέλουμε να εργαστούμε ταυτόχρονα σε περισσότερους από έναν κλάδους, μπορούμε να ρυθμίσουμε τοπικούς κλάδους να κάνουν dcommit
σε συγκεκριμένους κλάδους Subversion ξεκινώντας τους στην εισαγόμενη υποβολή Subversion για αυτόν τον κλάδο.
Αν θέλουμε έναν κλάδο opera
στον οποίο μπορούμε να εργαστούμε ξεχωριστά, μπορούμε να τρέξουμε:
$ git branch opera remotes/origin/opera
Τώρα, εάν θέλουμε να συγχωνεύσουμε τον κλάδο μας opera
σε trunk
(τον κύριο κλάδο μας), μπορούμε να το κάνουμε με μια κανονική git merge
.
Αλλά πρέπει να δώσουμε ένα περιγραφικό μήνυμα υποβολής (με -m
), αλλιώς η συγχώνευση θα λέει “Merge branch opera” αντί για κάτι χρήσιμο.
Ας θυμηθούμε ότι παρόλο που χρησιμοποιούμε την git merge
για να κάνουμε αυτήν τη λειτουργία και η συγχώνευση πιθανόν θα είναι πολύ πιο εύκολη από ό,τι θα ήταν στο Subversion (επειδή το Git θα ανιχνεύσει αυτόματα την κατάλληλη βάση συγχώνευσης για μας), αυτή δεν είναι μία κανονική υποβολή συγχώνευσης του Git.
Πρέπει να ωθήσουμε αυτά τα δεδομένα πίσω σε έναν διακομιστή Subversion που δεν μπορεί να χειριστεί μια υποβολή που παρακολουθεί περισσότερους από έναν γονείς· οπότε, αφού τα ωθήσουμε, θα μοιάζει με μια μία μοναδική υποβολή που στρίμωξε όλη τη δουλειά ενός άλλου κλάδου κάτω από μια και μοναδική υποβολή.
Αφού συγχωνεύσουμε έναν κλάδο σε κάποιον άλλο, δεν μπορούμε εύκολα να επιστρέψουμε και να συνεχίσουμε να εργαζόμαστε σε αυτόν τον κλάδο, όπως συνήθως μπορούμε στο Git.
Η εντολή dcommit
που τρέχουμε διαγράφει κάθε πληροφορία που λέει σε ποιον κλάδο συγχωνεύθηκε, έτσι οι μεταγενέστεροι υπολογισμοί βάσης συγχώνευσης θα είναι λανθασμένοι —η dcommit
κάνει το αποτέλεσμα git merge
να μοιάζει σαν να τρέξαμε git merge --squash
.
Δυστυχώς, δεν υπάρχει κάποιος καλός τρόπος για να αποφύγουμε αυτήν την κατάσταση —το Subversion δεν μπορεί να αποθηκεύσει αυτές τις πληροφορίες, έτσι θα είμαστε πάντα πάντα περιορισμένοι από τους περιορισμούς του, εφόσον το χρησιμοποιούμε στον διακομιστή μας.
Για να αποφύγουμε τα προβλήματα, πρέπει να διαγράψουμε τον τοπικό κλάδο (στην περίπτωση αυτήν τον opera
) αφού τον συγχωνεύσουμε στον κορμό.
Εντολές Subversion
Το σύνολο εργαλείων git svn
παρέχει πολλές εντολές για να διευκολύνει τη μετάβαση στο Git παρέχοντας κάποια λειτουργικότητα παρόμοια με αυτήν που είχαμε στο Subversion.
Ακολουθούν μερικές εντολές που μας δίνουν αυτά που έδινε το Subversion.
Μορφή ιστορικού στο SVN
Αν είμαστε συνηθισμένοι στο Subversion και θέλουμε να δούμε το ιστορικό μας σε στυλ SVN, μπορούμε να εκτελέσουμε την git svn log
για να δούμε το ιστορικό των εργασιών μας σε μορφή SVN:
$ git svn log
------------------------------------------------------------------------
r87 | schacon | 2014-05-02 16:07:37 -0700 (Sat, 02 May 2014) | 2 lines
autogen change
------------------------------------------------------------------------
r86 | schacon | 2014-05-02 16:00:21 -0700 (Sat, 02 May 2014) | 2 lines
Merge branch 'experiment'
------------------------------------------------------------------------
r85 | schacon | 2014-05-02 16:00:09 -0700 (Sat, 02 May 2014) | 2 lines
updated the changelog
Θα πρέπει να γνωρίζουμε δύο σημαντικά πράγματα για την git svn log
.
Πρώτον, λειτουργεί εκτός σύνδεσης, σε αντίθεση με την πραγματική εντολή svn log
, η οποία ζητάει από τον διακομιστή Subversion τα δεδομένα.
Δεύτερον, δείχνει μόνο υποβολές που έχουν υποβληθεί στον διακομιστή Subversion.
Τοπικές υποβολές του Git που δεν τις έχουμε κάνει dcommit ακόμα δεν εμφανίζονται· ούτε οι υποβολές που έχουν κάνει άλλοι στον διακομιστή Subversion στο μεταξύ.
Είναι περισσότερο σαν την τελευταία γνωστή κατάσταση των υποβολων στον διακομιστή Subversion.
Επισημειώσεις στο SVN
Καθώς η εντολή git svn log
προσομοιώνει την εντολή svn log
εκτός σύνδεσης, μπορούμε να πάρουμε το ισοδύναμο της svn annotate
εκτελώντας την git svn blame [αρχείο]
.
Η έξοδος μοιάζει ως εξής:
$ git svn blame README.txt
2 temporal Protocol Buffers - Google's data interchange format
2 temporal Copyright 2008 Google Inc.
2 temporal http://code.google.com/apis/protocolbuffers/
2 temporal
22 temporal C++ Installation - Unix
22 temporal =======================
2 temporal
79 schacon Committing in git-svn.
78 schacon
2 temporal To build and install the C++ Protocol Buffer runtime and the Protocol
2 temporal Buffer compiler (protoc) execute the following:
2 temporal
Και πάλι, δεν δείχνει υποβολές που κάναμε τοπικά στο Git ή που έχουν ωθηθεί στο Subversion εν τω μεταξύ.
Πληροφορίες διακομιστή SVN
Μπορούμε επίσης να αποκτήσουμε το ίδιο είδος πληροφορίας που μας δίνει η svn info
, τρέχοντας την git svn info
:
$ git svn info Path: . URL: https://schacon-test.googlecode.com/svn/trunk Repository Root: https://schacon-test.googlecode.com/svn Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029 Revision: 87 Node Kind: directory Schedule: normal Last Changed Author: schacon Last Changed Rev: 87 Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009)
Αυτό είναι σαν τις blame
και log
στο ότι εκτελείται εκτός σύνδεσης και είναι ενημερωμένο μόνο μέχρι την τελευταία φορά που επικοινωνήσαμε με τον διακομιστή Subversion.
Παράβλεψη των παραβλέψεων του Subversion
Αν κλωνοποιήσουμε ένα αποθετήριο Subversion που έχει τις ιδιότητες svn:ignore
που ορίζουμε οπουδήποτε, πιθανότατα θέλουμε να ορίσουμε αντίστοιχα αρχεία .gitignore
έτσι ώστε να μην υποβάλουμε κατά λάθος αρχεία που δεν θα έπρεπε.
Η git svn
έχει δύο εντολές για να βοηθήσει με αυτό το πρόβλημα.
Η πρώτη είναι η git svn create-ignore
, η οποία δημιουργεί αυτόματα τα αντίστοιχα αρχεία .gitignore
για μας, έτσι ώστε η επόμενη υποβολή να τα συμπεριλάβει.
Η δεύτερη εντολή είναι η git svn show-ignore
, η οποία εκτυπώνει στην stdout τις γραμμές που πρέπει να βάλουμε σε ένα αρχείο .gitignore
ώστε να μπορούμε να ανακατευθύνουμε την έξοδο στο αρχείο εξαιρέσεων του έργου:
$ git svn show-ignore > .git/info/exclude
Με αυτόν τον τρόπο, δεν γεμίζουμε το έργο με αρχεία .gitignore
.
Αυτή είναι μια καλή επιλογή αν είμαστε ο μόνος χρήστης Git σε μια ομάδα Subversion και οι συνεργάτες μας δεν θέλουν αρχεία .gitignore
στο έργο.
Περίληψη Git-SVN
Τα εργαλεία git svn
είναι χρήσιμα αν είμαστε κολλημένοι με έναν διακομιστή Subversion ή βρισκόμαστε σε περιβάλλον ανάπτυξης που απαιτεί τη χρήση διακομιστή Subversion.
Θα πρέπει να το δούμε σαν σακατεμένο Git αλλιώς θα συνατήσουμε ζητήματα στη μετάφραση από το ένα σύστημα στο άλλο που μπορεί να μπερδέψουν εμάς και τους συνεργάτες μας.
Για να αποφύγουμε τα προβλήματα, καλό είναι να ακολουθούμε αυτές τις οδηγίες:
-
Διατηρούμε ένα γραμμικό ιστορικό Git που δεν περιέχει υποβολές συγχώνευσης που έγιναν με την
git merge
. Κάθε εργασία που κάνουμε έξω από τον κλάδο της κύριας γραμμής την επανατοποθετούμε ξανά σε αυτήν (αλλάζουμε τη βάση της στην κύρια γραμμή)· δεν τη συγχωνεύουμε. -
Δεν εγκαθιστούμε και συνεργαζόμαστε σε ξεχωριστό διακομιστή Git. Ενδεχομένως έχουμε έναν για να επιταχύνουμε τους κλώνους για νέους προγραμματιστές, αλλά δεν ωθούμε τίποτα σε αυτόν που δεν έχει καταχώρηση
git-svn-id
. Ίσως ακόμα θελήσουμε να προσθέσουμε ένα άγκιστροpre-receive
που ελέγχει κάθε μήνυμα αποστολής για έναgit-svn-id
και απορρίπτει ωθήσεις που περιέχουν υποβολές χωρίς αυτό.
Εάν ακολουθήσουμε αυτές τις οδηγίες, η συνεργασία με έναν διακομιστή Subversion μπορεί να είναι πιο ανεκτή. Ωστόσο, αν είναι δυνατό να μετακινηθούμε σε έναν πραγματικό διακομιστή Git, αυτό θα ωφελήσει πολυ πολύ περισσότερο την ομάδα μας.
Git και Mercurial
Το σύμπαν των DVCS είναι μεγαλύτερο από το Git. Στην πραγματικότητα, υπάρχουν πολλά άλλα συστήματα σε αυτό το σύμπαν, το καθένα με τη δική του οπτική για το πώς γίνεται σωστά ο κατανεμημένος έλεγχος εκδόσεων. Πέρα από το Git, το πιο δημοφιλές είναι το Mercurial, και μάλιστα και δύο συστήματα είναι πολύ παρόμοια από πολλές απόψεις.
Τα καλά νέα, εφόσον προτιμάμε τη συμπεριφορά από την πλευρά του πελάτη του Git αλλά εργαζόμαστε με ένα έργο του οποίου ο πηγαίος κώδικας ελέγχεται με το Mercurial, είναι ότι υπάρχει ένας τρόπος να χρησιμοποιήσουμε το Git ως πελάτη για ένα αποθετήριο που φιλοξενείται από Mercurial.
Δεδομένου ότι ο τρόπος με τον οποίο το Git μιλάει με αποθετήρια διακομιστών μέσω απομακρυσμένων, δεν πρέπει να αποτελεί έκπληξη ότι αυτή η γέφυρα έχει υλοποιηθεί ως απομακρυσμένος βοηθός.
Το όνομα του έργου είναι git-remote-hg
και μπορεί να βρεθεί στη https://github.com/felipec/git-remote-hg.
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 <ben@straub.cc>
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 <ben@straub.cc>
| date: Thu Aug 14 20:21:09 2014 -0700
| summary: A permanent change
|
| @ changeset: 6:8f65e5e02793
|/ branch: develop
| user: Ben Straub <ben@straub.cc>
| 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 <ben@straub.cc>
| | 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, οπότε προσπαθούμε να το αποφύγουμε.
Ανακεφαλαίωση Mercurial
Τα Git και Mercurial είναι αρκετά παρόμοια ώστε η συνεργασία μεταξύ τους είναι αρκετά ανώδυνη. Εάν αποφύγουμε να αλλάξουμε το ιστορικό που έχει αφήσει το μηχάνημά μας (όπως συνήθως συνιστάται), ίσως να μην γνωρίζουμε καν ότι το άλλο άκρο είναι Mercurial.
Git and Perforce
Το Perforce είναι ένα πολύ δημοφιλές σύστημα ελέγχου εκδόσεων σε εταιρικά περιβάλλοντα. Κυκλοφορεί από το 1995, γεγονός που το καθιστά το παλαιότερο σύστημα που καλύπτεται σε αυτό το κεφάλαιο. Ως εκ τούτου, έχει σχεδιαστεί με τους περιορισμούς της εποχής του. Υποθέτει ότι είμαστε πάντα συνδεδεμένοι σε έναν κεντρικό διακομιστή και ότι διατηρείται μόνο μία έκδοση στον τοπικό δίσκο. Βεβαίως, οι λειτουργίες και οι περιορισμοί του είναι κατάλληλα για αρκετά συγκεκριμένα προβλήματα, αλλά υπάρχουν πολλά έργα που χρησιμοποιούν το Perforce στα οποία το Git θα λειτουργούσε πραγματικά καλύτερα.
Υπάρχουν δύο επιλογές αν θέλουμε να συνδυάσουμε τη χρήση του Perforce και του Git. Η πρώτη που θα καλύψουμε είναι η γέφυρα “Git Fusion” από τους κατασκευαστές του Perforce, που μας επιτρέπει να εκθέτουμε υποδέντρα μίας αποθήκης Perforce ως αποθετήρια Git για ανάγνωση και εγγραφή. Το δεύτερο είναι το git-p4, μια γέφυρα από την πλευρά του πελάτη, που μας επιτρέπει να χρησιμοποιήσουμε το Git ως πελάτη του Perforce, χωρίς να απαιτήσουμε νέα διαμόρφωση του διακομιστή Perforce.
Git Fusion
Το Perforce παρέχει ένα προϊόν που ονομάζεται Git Fusion (διαθέσιμο στη http://www.perforce.com/git-fusion), το οποίο συγχρονίζει έναν διακομιστή Perforce με αποθετήρια Git στην πλευρά του διακομιστή.
Εγκατάσταση
Για τα παραδείγματα μας, θα χρησιμοποιήσουμε την πιο εύκολη μέθοδο εγκατάστασης για το Git Fusion, η οποία είναι να κατεβάσουμε μία εικονική μηχανή (virtual machine) που τρέχει το δαίμονα Perforce και το Git Fusion. Μπορούμε να πάρουμε την εικόνα της εικονικής μηχανής από το http://www.perforce.com/downloads/Perforce/20-User, και μόλις ολοκληρωθεί η λήψη, την εισάγουμε το στο αγαπημένο μας λογισμικό εικονικοποίησης (θα χρησιμοποιήσουμε το VirtualBox).
Με την πρώτη εκκίνηση του μηχανήματος, μάς ζητά να προσαρμόσουμε τον κωδικό πρόσβασης για τρεις χρήστες Linux (root
, perforce
και git
) και να δώσουμε ένα όνομα στιγμιότυπου, το οποίο μπορεί να χρησιμοποιηθεί για να διακρίνει αυτήν την εγκατάσταση από άλλους στο ίδιο δίκτυο.
Όταν όλα αυτά ολοκληρωθούν, θα δούμε τα εξής:

Θα πρέπει να λάβουμε υπόψη τη διεύθυνση IP που εμφανίζεται εδώ, θα τη χρησιμοποιήσουμε αργότερα.
Στη συνέχεια, θα δημιουργήσουμε έναν χρήστη Perforce.
Επιλέγουμε “Login” στο κάτω μέρος και πατάμε Enter (ή SSH στο μηχάνημα) και συνδεόμαστεε ως root
.
Στη συνέχεια, χρησιμοποιούμε αυτές τις εντολές για να δημιουργήσουμε έναν χρήστη:
$ p4 -p localhost:1666 -u super user -f john
$ p4 -p localhost:1666 -u john passwd
$ exit
H πρώτη θα ανοίξει έναν επεξεργαστή VI για να εξατομικεύσει τον χρήστη, αλλά μπορούμε να αποδεχτούμε τις προεπιλογές πληκτρολογώντας :wq
και πατώντας Enter.
Η δεύτερη θα μας ζητήσει να εισαγάγουμε έναν κωδικό πρόσβασης δύο φορές.
Αυτό είναι όλο που πρέπει να κάνουμε στο κέλυφος, οπότε βγαίνουμε από τη συνεδρία.
Το επόμενο πράγμα που πρέπει να κάνουμε είναι να πούμε στο Git να μην επαληθεύει τα πιστοποιητικά SSL. Η εικόνα Git Fusion έρχεται με πιστοποιητικό, αλλά είναι για έναν domain που δεν ταιριάζει με τη διεύθυνση IP της εικονικής μηχανής μας, οπότε το Git θα απορρίψει τη σύνδεση HTTPS. Εάν πρόκειται να γίνει μόνιμη εγκατάσταση, συμβουλευόμαστε το εγχειρίδιο του Perforce Git Fusion για να εγκαταστήσουμε ένα διαφορετικό πιστοποιητικό· για τον σκοπό των παραδειγμάτων μας, αυτό αρκεί:
$ export GIT_SSL_NO_VERIFY=true
Τώρα μπορούμε να δοκιμάσουμε ότι όλα λειτουργούν.
$ git clone https://10.0.1.254/Talkhouse
Cloning into 'Talkhouse'...
Username for 'https://10.0.1.254': john
Password for 'https://john@10.0.1.254':
remote: Counting objects: 630, done.
remote: Compressing objects: 100% (581/581), done.
remote: Total 630 (delta 172), reused 0 (delta 0)
Receiving objects: 100% (630/630), 1.22 MiB | 0 bytes/s, done.
Resolving deltas: 100% (172/172), done.
Checking connectivity... done.
Η εικόνα εικονικής μηχανής έρχεται εξοπλισμένη με ένα παράδείγμα έργου που μπορούμε να κλωνοποιήσουμε.
Εδώ κλωνοποιούμε μέσα από το HTTPS, με τον χρήστη john
που δημιουργήσαμε παραπάνω. Το Git ζητά διαπιστευτήρια για αυτήν τη σύνδεση, αλλά η προσωρινή μνήμη των διαπιστευτηρίων θα μας επιτρέψει να παραλείψουμε αυτό το βήμα για τυχόν μεταγενέστερα αιτήματα.
Διαμόρφωση του Git Fusion
Μόλις εγκαταστήσουμε το Git Fusion, θα χρειαστεί να τροποποιήσουμε τη διαμόρφωση.
Αυτό γίνεται πραγματικά πολύ εύκολα χρησιμοποιώντας οποιονδήποτε πελάτη Perforce· απλά απεικονίζουμε τον κατάλογο //.git-fusion
στον διακομιστή Perforce στον χώρο εργασίας μας.
Η δομή του αρχείου είναι η εξής:
$ tree
.
├── objects
│ ├── repos
│ │ └── [...]
│ └── trees
│ └── [...]
│
├── p4gf_config
├── repos
│ └── Talkhouse
│ └── p4gf_config
└── users
└── p4gf_usermap
498 directories, 287 files
Ο κατάλογος objects
χρησιμοποιείται εσωτερικά από το Git Fusion για να απεικονίσει αντικείμενα Perforce στο Git και το αντίστροφο, δεν θα χρειαστεί να κάνουμε τίποτα εκεί.
Υπάρχει ένα καθολικό αρχείο p4gf_config
σε αυτόν τον κατάλογο, καθώς και ένα για κάθε αποθετήριο —αυτά είναι τα αρχεία διαμόρφωσης που καθορίζουν τον τρόπο συμπεριφοράς του Git Fusion.
Ας ρίξουμε μια ματιά στο αρχείο στη ρίζα:
[repo-creation]
charset = utf8
[git-to-perforce]
change-owner = author
enable-git-branch-creation = yes
enable-swarm-reviews = yes
enable-git-merge-commits = yes
enable-git-submodules = yes
preflight-commit = none
ignore-author-permissions = no
read-permission-check = none
git-merge-avoidance-after-change-num = 12107
[perforce-to-git]
http-url = none
ssh-url = none
[@features]
imports = False
chunked-push = False
matrix2 = False
parallel-push = False
[authentication]
email-case-sensitivity = no
Δεν θα εξηγήσουμε τι είναι η κάθε σημαία εδώ, αλλά ας σημειωθεί ότι αυτό είναι απλά ένα αρχείο κειμένου INI, όπως αυτά που χρησιμοποιεί το Git για τη διαμόρφωση.
Αυτό το αρχείο καθορίζει τις γενικές επιλογές, οι οποίες μπορούν στη συνέχεια να αντικατασταθούν από συγκεκριμένα αρχεία διαμόρφωσης για κάθε αποθετήριο, όπως repos/Talkhouse/p4gf_config
.
Εάν ανοίξουμε αυτό το αρχείο, θα δούμε μια ενότητα [@repo]
με ορισμένες ρυθμίσεις που διαφέρουν από τις καθολικές προεπιλογές.
Θα δούμε επίσης τμήματα που μοιάζουν με αυτά:
[Talkhouse-master]
git-branch-name = master
view = //depot/Talkhouse/main-dev/... ...
Πρόκειται για μία απεικόνιση μεταξύ κλάδου Perforce και κλάδου Git.
Η ενότητα μπορεί να ονομαστεί ό,τι θέλουμε, αρκεί το όνομα να είναι μοναδικό.
Η git-branch-name
μάς επιτρέπει να μετατρέψουμε μια διαδρομή αποθήκης που θα ήταν δύσχρηστη στο Git σε ένα πιο φιλικό όνομα.
Η ρύθμιση view
ελέγχει τον τρόπο απεικόνισης των αρχείων Perforce στο αποθετήριο Git χρησιμοποιώντας τη σύνταξη απεικόνισης τυπικής προβολής.
Είναι δυνατό να καθοριστούν περισσότερες από μία απεικονίσεις, όπως σε αυτό το παράδειγμα:
[multi-project-mapping]
git-branch-name = master
view = //depot/project1/main/... project1/...
//depot/project2/mainline/... project2/...
Με αυτόν τον τρόπο, εάν η κανονική αντιστοίχιση του χώρου εργασίας μας περιλαμβάνει αλλαγές στη δομή των καταλόγων, μπορούμε να τις αναπαράγουμε με ένα αποθετήριο Git.
Το τελευταίο αρχείο που θα συζητήσουμε είναι το users/p4gf_usermap
, το οποίο απεικονίζει χρήστες του Perforce σε χρήστες του Git και ίσως δεν χρειάζεται καν.
Κατά τη μετατροπή από μια σειρά αλλαγών Perforce σε μια υποβολή Git, η προεπιλεγμένη συμπεριφορά του Git Fusion είναι να αναζητήσει τον χρήστη του Perforce και να χρησιμοποιήσει τη διεύθυνση e-mail και το πλήρες όνομα που έχει αποθηκευτεί εκεί για το πεδίο συγγραφέα/υποβάλλοντος στο Git.
Κατά την αντίστροφη μετατροπή, η προεπιλογή είναι να αναζητήσουμε τον χρήστη Perforce με τη διεύθυνση e-mail που είναι αποθηκευμένη στο πεδίο συγγραφέα της υποβολής Git και να υποβάλουμε το σύνολο αλλαγών ως αυτός ο χρήστης (με την εφαρμογή των αντίστοιχων δικαιωμάτων).
Στις περισσότερες περιπτώσεις, αυτή η συμπεριφορά θα δουλέψει μια χαρά, αλλά ας εξετάσουμε το ακόλουθο αρχείο απεικόνισης:
john john@example.com "John Doe"
john johnny@appleseed.net "John Doe"
bob employeeX@example.com "Anon X. Mouse"
joe employeeY@example.com "Anon Y. Mouse"
Κάθε γραμμή έχει τη μορφή <χρήστης> <email> '<πλήρες όνομα>"
και δημιουργεί μία μόνο απεικόνιση χρήστη.
Οι δύο πρώτες γραμμές αντιστοιχίζονται σε δύο διαφορετικές διευθύνσεις e-mail στον ίδιο λογαριασμό χρήστη Perforce.
Αυτό είναι χρήσιμο αν έχουμε δημιουργήσει υποβολές Git κάτω από πολλές διαφορετικές διευθύνσεις e-mail (ή να αλλάξαμε διεύθυνση e-mail), αλλά θέλουμε να αντιστοιχιστούν στον ίδιο χρήστη Perforce.
Κατά τη δημιουργία μιας υποβολής Git από ένα σύνολο αλλαγών Perforce, η πρώτη γραμμή που ταιριάζει με τον χρήστη Perforce χρησιμοποιείται για τις πληροφορίες συγγραφέων Git.
Οι τελευταίες δύο γραμμές καλύπτουν τα πραγματικά ονόματα και τις διευθύνσεις e-mail του Bob και του Joe από τις υποβολές του Git που δημιουργούνται. Αυτό είναι ωραίο αν θέλουμε να ανοίξουμε ένα εσωτερικό έργο, αλλά δεν θέλουμε να δημοσιεύσουμε τον κατάλογο των υπαλλήλων μας σε ολόκληρο τον κόσμο. Ας σημειωθεί ότι οι διευθύνσεις e-mail και τα πλήρη ονόματα θα πρέπει να είναι μοναδικά, εκτός αν θέλουμε όλες οι υποβολές του Git να αποδοθούν σε ένα μόνο φανταστικό συγγραφέα.
Ροή εργασίας
Το Perforce Git Fusion είναι μια αμφίδρομη γέφυρα μεταξύ των ελέγχων εκδόσεων Perforce και Git. Ας ρίξουμε μια ματιά στο τι αίσθηση δημιουργείται όταν εργαζόμαστε από την πλευρά του Git. Υποθέτουμε ότι έχουμε απεικονίσει το έργο “Jam” χρησιμοποιώντας ένα αρχείο διαμόρφωσης όπως φαίνεται παραπάνω, τον οποίο μπορούμε να κλωνοποιήσουμε ως εξής:
$ git clone https://10.0.1.254/Jam
Cloning into 'Jam'...
Username for 'https://10.0.1.254': john
Password for 'https://ben@10.0.1.254':
remote: Counting objects: 2070, done.
remote: Compressing objects: 100% (1704/1704), done.
Receiving objects: 100% (2070/2070), 1.21 MiB | 0 bytes/s, done.
remote: Total 2070 (delta 1242), reused 0 (delta 0)
Resolving deltas: 100% (1242/1242), done.
Checking connectivity... done.
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/rel2.1
$ git log --oneline --decorate --graph --all
* 0a38c33 (origin/rel2.1) Create Jam 2.1 release branch.
| * d254865 (HEAD, origin/master, origin/HEAD, master) Upgrade to latest metrowerks on Beos -- the Intel one.
| * bd2f54a Put in fix for jam's NT handle leak.
| * c0f29e7 Fix URL in a jam doc
| * cc644ac Radstone's lynx port.
[...]
Την πρώτη φορά που το κάνουμε αυτό, ίσως χρειαστεί λίγος χρόνος. Αυτό που συμβαίνει είναι ότι το Git Fusion μετατρέπει όλα τα εφαρμοστέες σύνολα αλλαγών στο ιστορικό Perforce σε υποβολές Git. Αυτό συμβαίνει τοπικά στον διακομιστή, επομένως είναι σχετικά γρήγορο, αλλά αν έχουμε εκτενές ιστορικό, μπορεί να χρειαστεί λίγος χρόνος. Οι μεταγενέστερες ανακτήσεις πραγματοποιούν τμηματική μετατροπή, έτσι θα έχουμε την αίσθηση της εγγενούς ταχύτητας του Git.
Όπως μπορούμε να δούμε, το αποθετήριό μας μοιάζει ακριβώς όπως οποιοδήποτε άλλο αποθετήριο Git με το οποίο μπορούμε να εργαστούμε.
Υπάρχουν τρεις κλάδοι και το Git δημιούργησε έναν τοπικό κλάδο master
που παρακολουθεί τον origin/master
.
Ας κάνουμε λίγη δουλίτσα και να δημιουργήσουμε μερικές νέες υποβολές:
# ...
$ git log --oneline --decorate --graph --all
* cfd46ab (HEAD, master) Add documentation for new feature
* a730d77 Whitespace
* d254865 (origin/master, origin/HEAD) Upgrade to latest metrowerks on Beos -- the Intel one.
* bd2f54a Put in fix for jam's NT handle leak.
[...]
Έχουμε δύο νέες υποβολές. Τώρα ας ελέγξουμε αν έχει δουλεψει και κάποιος άλλος:
$ git fetch
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://10.0.1.254/Jam
d254865..6afeb15 master -> origin/master
$ git log --oneline --decorate --graph --all
* 6afeb15 (origin/master, origin/HEAD) Update copyright
| * cfd46ab (HEAD, master) Add documentation for new feature
| * a730d77 Whitespace
|/
* d254865 Upgrade to latest metrowerks on Beos -- the Intel one.
* bd2f54a Put in fix for jam's NT handle leak.
[...]
Φαίνεται ότι κάποιος δούλεψε!
Δεν θα το γνωρίζατε από αυτήν την προβολή, αλλά η υποβολή 6afeb15
δημιουργήθηκε στην πραγματικότητα χρησιμοποιώντας έναν πελάτη Perforce.
Απλώς μοιάζει με μια άλλη υποβολή από τη σκοπιά του Git, και ακριβώς αυτό είναι που θέλουμε.
Ας δούμε πώς αντιμετωπίζει ο διακομιστής Perforce μια υποβολή συγχώνευσης:
$ git merge origin/master
Auto-merging README
Merge made by the 'recursive' strategy.
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done.
Total 9 (delta 6), reused 0 (delta 0)
remote: Perforce: 100% (3/3) Loading commit tree into memory...
remote: Perforce: 100% (5/5) Finding child commits...
remote: Perforce: Running git fast-export...
remote: Perforce: 100% (3/3) Checking commits...
remote: Processing will continue even if connection is closed.
remote: Perforce: 100% (3/3) Copying changelists...
remote: Perforce: Submitting new Git commit objects to Perforce: 4
To https://10.0.1.254/Jam
6afeb15..89cba2b master -> master
Το Git νομίζει ότι λειτούργησε.
Ας ρίξουμε μια ματιά στο ιστορικό του αρχείου README
από τη σκοπιά του Perforce, χρησιμοποιώντας τη λειτουργία του γραφήματος αναθεώρησης του p4v
:

Εάν δεν έχουμε δει ποτέ αυτήν την προβολή προηγουμένως, μπορεί να φαίνεται συγκεχυμένη, αλλά δείχνει τις ίδιες έννοιες με ένα γραφικό πρόγραμμα προβολής του ιστορικού του Git.
Εξετάζουμε το ιστορικό του αρχείου README
, έτσι ώστε το δέντρο καταλόγων πάνω αριστερά να δείχνει μόνο αυτό το αρχείο καθώς αναδύεται σε διάφορους κλάδους.
Στην επάνω δεξιά στήλη, έχουμε ένα οπτικό γράφημα του πώς σχετίζονται οι διάφορες αναθεωρήσεις του αρχείου και η ευρύτερη εικόνα αυτού του γραφήματος βρίσκεται στο κάτω δεξί μέρος.
Η υπόλοιπη προβολή δίνεται στην προβολή λεπτομερειών για την επιλεγμένη αναθεώρηση (2
σε αυτήν την περίπτωση).
Ένα πράγμα που πρέπει να παρατηρήσουμε είναι ότι το γράφημα μοιάζει ακριβώς με το ιστορικό του Git.
Το Perforce δεν είχε όνομα κλάδου για να αποθηκεύσει τις υποβολές 1
και` 2`, έτσι έκανε έναν “ανώνυμο” κλάδο στον κατάλογο .git-fusion
για να τις κρατήσει.
Αυτό θα συμβεί επίσης για τα κλάδους Git με όνομα που δεν αντιστοιχίζονται σε έναν κλάδο Perforce με όνομα (και μπορούμε αργότερα να τους αεπικονίσουμε σε κλάδο Perforce χρησιμοποιώντας το αρχείο διαμόρφωσης).
Τα περισσότερα από αυτά συμβαίνουν στο παρασκήνιο, αλλά το τελικό αποτέλεσμα είναι ότι ένα άτομο σε μια ομάδα μπορεί να χρησιμοποιεί το Git, άλλο μπορεί να χρησιμοποιεί το Perforce και κανένας από αυτούς δεν θα γνωρίζει την επιλογή του άλλου.
Ανακεφαλαίωση Git-Fusion
Εάν έχουμε (ή μπορούμε να αποκτήσουμε) πρόσβαση στον διακομιστή Perforce μας, το Git Fusion είναι ένας πολύ καλός τρόπος για να κάνουμε το Git και το Perforce να μιλάνε το ένας στο άλλο. Εμπλέκεται η διαμόρφωση κάποιων ρυθμίσεων, αλλά η καμπύλη μάθησης δεν είναι πολύ απότομη. Αυτή είναι μία από τις λίγες ενότητες αυτού του κεφαλαίου όπου δεν θα εμφανιστούν προειδοποιήσεις σχετικά με τη χρήση της πλήρους ισχύος του Git. Αυτό δεν σημαίνει ότι το Perforce θα είναι ευχαριστημένο από όλα όσα ρίχνουμε σε αυτό —αν προσπαθήσουμε να ξαναγράψουμε ιστορικό που έχει ήδη ωθηθεί, το Git Fusion θα την απορρίψει— αλλά το Git Fusion προσπαθεί πολύ σκληρά να νιώσει μέρος του Git. Μπορούμε ακόμα να χρησιμοποιήσουμε τις λειτουργικές υπομονάδες Git (αν και θα φαίνονται περίεργες στους χρήστες του Perforce) και να συγχωνεύσουμε κλάδους (αυτό θα καταγραφεί ως ενσωμάτωση στην πλευρά Perforce).
Εάν δεν μπορούμε να πείσουμε τον διαχειριστή του διακομιστή μας να εγκαταστήσει το Git Fusion, υπάρχει ακόμα ένας τρόπος να χρησιμοποιήσουμε αυτά τα εργαλεία μαζί.
git-p4
Η git-p4
είναι μια αμφίδρομη γέφυρα μεταξύ Git και Perforce.
Τρέχει εξ ολοκλήρου μέσα στο αποθετήριο Git, επομένως δεν θα χρειαστεί κανενός είδους πρόσβαση στον διακομιστή Perforce (εκτός από τα διαπιστευτήρια χρήστη, φυσικά).
Η git-p4
δεν είναι τόσο ευέλικτο ούτε ολοκληρωμένο ως λύση όπως το Git Fusion, αλλά μας επιτρέπει να κάνουμε τα περισσότερα από όσα θέλουμε να κάνουμε χωρίς να επεμβαίνουμε στο περιβάλλον του διακομιστή.
Note
|
Θα χρειαστούμε το εργαλείο |
Εγκατάσταση
Για παράδειγμα, θα τρέχουμε τον διακομιστή Perforce από το Git Fusion OVA, όπως φαίνεται παραπάνω, αλλά θα παρακάμψουμε τον διακομιστή Git Fusion και θα πάμε κατευθείαν στον έλεγχο έκδοσης Perforce.
Για να χρησιμοποιήσουμε τον πελάτη γραμμής εντολών p4
(από τον οποίο εξαρτάται το git-p4), θα χρειαστεί να ορίσουμε μερικές μεταβλητές περιβάλλοντος:
$ export P4PORT=10.0.1.254:1666
$ export P4USER=john
Ξεκινώντας
Όπως και με ο,τιδήποτε στο Git, η πρώτη εντολή είναι να κλωνοποιήσουμε:
$ git p4 clone //depot/www/live www-shallow
Importing from //depot/www/live into www-shallow
Initialized empty Git repository in /private/tmp/www-shallow/.git/
Doing initial import of //depot/www/live/ from revision #head into refs/remotes/p4/master
Αυτό δημιουργεί ό,τι με όρους Git καλείτια “ρηχός” κλώνος· μόνο η τελευταία αναθεώρηση Perforce εισάγεται στο Git. Ας θυμηθούμε το Perforce δεν έχει σχεδιαστεί για να παρέχει κάθε αναθεώρηση σε κάθε χρήστη. Αυτό αρκεί για να χρησιμοποιήσουμε το Git ως πελάτη Perforce, αλλά για άλλους σκοπούς δεν είναι αρκετό.
Μόλις τελειώσει, έχουμε έναν πλήρως λειτουργικό αποθετήριο Git:
$ cd myproject
$ git log --oneline --all --graph --decorate
* 70eaf78 (HEAD, p4/master, p4/HEAD, master) Initial import of //depot/www/live/ from the state at revision #head
ας σημειωθεί επίσης ότι υπάρχει μία απομακρυσμένη αποθήκη p4
για τον διακομιστή Perforce, όμως όλα τα άλλα μοιάζουν με έναν τυπικό κλώνο.
Στην πραγματικότητα, αυτό είναι λίγο παραπλανητικό· δεν υπάρχει πραγματικά μία απομακρυσμένη αποθήκη εκεί.
$ git remote -v
Δεν υπάρχουν καθόλου απομακρυσμένες μονάδες σε αυτό το αποθετήριο.
Η git-p4
δημιούργησε κάποιες ref ώστε να αναπαριστά την κατάσταση του διακομιστή και αυτές μοιάζουν με απομακρυσμένες αναφορές στην git log
, αλλά δεν τις διαχειρίζεται το ίδιο το Git και δεν μπορούμε να τις ωθήσουμε.
Ροή εργασίας
Ας δουλέψουμε, λοιπόν. Ας υποθέσουμε ότι έχουμε σημειώσει κάποια πρόοδο σε μία πολύ σημαντική λειτουργία και είμαστε έτοιμοι να τη δείξουμε στην υπόλοιπη ομάδα μας.
$ git log --oneline --all --graph --decorate
* 018467c (HEAD, master) Change page title
* c0fb617 Update link
* 70eaf78 (p4/master, p4/HEAD) Initial import of //depot/www/live/ from the state at revision #head
Έχουμε κάνει δύο νέες υποβολές ότι είμαστε έτοιμοι να υποβάλουμε στον διακομιστή Perforce. Ας ελέγξουμε αν κάποιος άλλος δούλευε σήμερα:
$ git p4 sync
git p4 sync
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
Import destination: refs/remotes/p4/master
Importing revision 12142 (100%)
$ git log --oneline --all --graph --decorate
* 75cd059 (p4/master, p4/HEAD) Update copyright
| * 018467c (HEAD, master) Change page title
| * c0fb617 Update link
|/
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
Φαίνεται ότι κάποιος δούλευε και οι master
και p4/master
έχουν αποκλίνει.
Το σύστημα διακλάδωσης της Perforce δεν είναι όπως του Git, οπότε οι υποβολές συγχώνευσης στερούνται νοήματος.
Η git-p4
συνιστά να αλλάξουμε τη βάση των υποβολών μας και μάλιστα μας παρέχει και μια συντόμευση για να το κάνουμε:
$ git p4 rebase
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
No changes to import!
Rebasing the current branch onto remotes/p4/master
First, rewinding head to replay your work on top of it...
Applying: Update link
Applying: Change page title
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Είναι ίσως φανερό από την έξοδο, αλλά η git p4 rebase
είναι μια συντόμευση για το git p4 sync
ακολουθούμενη από git rebase p4/master
.
Στην πραγματικότητα είναι λίγο πιο έξυπνο από αυτό, ειδικά όταν εργαζόμαστε με πολλαπλούς κλάδους, αλλά αυτή είναι μια καλή προσέγγιση.
Τώρα το ιστορικό μας είναι και πάλι γραμμικό και είμαστε έτοιμοι να συνεισφέρουμε τις αλλαγές μας στο Perforce.
Η εντολή git p4 submit
θα προσπαθήσει να δημιουργήσει μια νέα έκδοση Perforce για κάθε υποβολή Git μεταξύ του p4/master
και master
.
Αν την τρέξουμε θα ανοίξει τον αγαπημένο μας επεξεργαστή και το περιεχόμενο του αρχείου θα είναι κάτι σαν αυτό:
# A Perforce Change Specification.
#
# Change: The change number. 'new' on a new changelist.
# Date: The date this specification was last modified.
# Client: The client on which the changelist was created. Read-only.
# User: The user who created the changelist.
# Status: Either 'pending' or 'submitted'. Read-only.
# Type: Either 'public' or 'restricted'. Default is 'public'.
# Description: Comments about the changelist. Required.
# Jobs: What opened jobs are to be closed by this changelist.
# You may delete jobs from this list. (New changelists only.)
# Files: What opened files from the default changelist are to be added
# to this changelist. You may delete files from this list.
# (New changelists only.)
Change: new
Client: john_bens-mbp_8487
User: john
Status: new
Description:
Update link
Files:
//depot/www/live/index.html # edit
######## git author ben@straub.cc does not match your p4 account.
######## Use option --preserve-user to modify authorship.
######## Variable git-p4.skipUserNameCheck hides this message.
######## everything below this line is just the diff #######
--- //depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000
+++ /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/index.html 2014-08-31 18:26:05.000000000 0000
@@ -60,7 +60,7 @@
</td>
<td valign=top>
Source and documentation for
-<a href="http://www.perforce.com/jam/jam.html">
+<a href="jam.html">
Jam/MR</a>,
a software build tool.
</td>
Αυτό είναι ως επί το πλείστον το ίδιο περιεχόμενο που θα βλέπαμε αν τρέχαμε την p4 submit
, εκτός από τα πράγματα στο τέλος που η git-p4
έχει συμπεριλάβει.
Η git-p4
προσπαθεί να τιμά τις ρυθμίσεις τόσο του Git όσο και του Perforce όταν πρέπει να παράσχει ένα όνομα για μια υποβολή ή ένα σύνολο αλλαγών, αλλά σε ορισμένες περιπτώσεις θέλουμε να τις παρκάμψουμε.
Για παράδειγμα, εάν η υποβολή Git που εισάγουμε γράφτηκε από έναν συνεισφέροντα που δεν διαθέτει λογαριασμό χρήστη Perforce, ίσως θέλουμε να φαίνεται ότι αυτός έγραψε τα προκύπτοντα σύνολα αλλαγών (και όχι εμείς).
Η git-p4
εισήγαγε με προσοχή το μήνυμα από την υποβολή Git ως το περιεχόμενο αυτού του συνόλου αλλαγών του Perforce, οπότε το μόνο που έχουμε να κάνουμε είναι να αποθηκεύσουμε και να το βγούμε δύο φορές (μία φορά για κάθε υποβολή).
Η έξοδος που εκτυπώνεται στο κέλυφος θα φαίνεται κάπως έτσι:
$ git p4 submit
Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Synchronizing p4 checkout...
... - file(s) up-to-date.
Applying dbac45b Update link
//depot/www/live/index.html#4 - opened for edit
Change 12143 created with 1 open file(s).
Submitting change 12143.
Locking 1 files ...
edit //depot/www/live/index.html#5
Change 12143 submitted.
Applying 905ec6a Change page title
//depot/www/live/index.html#5 - opened for edit
Change 12144 created with 1 open file(s).
Submitting change 12144.
Locking 1 files ...
edit //depot/www/live/index.html#6
Change 12144 submitted.
All commits applied!
Performing incremental import into refs/remotes/p4/master git branch
Depot paths: //depot/www/live/
Import destination: refs/remotes/p4/master
Importing revision 12144 (100%)
Rebasing the current branch onto remotes/p4/master
First, rewinding head to replay your work on top of it...
$ git log --oneline --all --graph --decorate
* 775a46f (HEAD, p4/master, p4/HEAD, master) Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
Το αποτέλεσμα είναι σαν να κάναμε μία git push
, που είναι και η κοντινότερη αναλογία με αυτό που πραγματικά συνέβη.
Υπόψη ότι κατά τη διάρκεια αυτής της διαδικασίας, κάθε υποβολή Git μετατρέπεται σε ένα σύνολο αλλαγών Perforce· αν θέλουμε να τις στριμώξουμε σε ένα ενιαίο σύνολο αλλαγών, μπορούμε να το κάνουμε αυτό με μια διαδραστική αλλαγή βάσης πριν τρέξουμε την git p4 submit
.
Επίσης, ας σημειωθεί ότι οι αριθμοί SHA-1 όλων των υποβολών που υποβλήθηκαν ως σύνολα αλλαγών έχουν αλλάξει· αυτό συμβαίνει επειδή η git-p4
προσθέτει μια γραμμή στο τέλος κάθε υποβολής που μετατρέπει:
$ git log -1
commit 775a46f630d8b46535fc9983cf3ebe6b9aa53145
Author: John Doe <john@example.com>
Date: Sun Aug 31 10:31:44 2014 -0800
Change page title
[git-p4: depot-paths = "//depot/www/live/": change = 12144]
Τι θα συμβεί αν προσπαθήσουμε να υποβάλουμε μια υποβολή συγχώνευσης; Ας το δοκιμάσουμε. Ακολουθεί η κατάσταση στην οποία βρισκόμαστε:
$ git log --oneline --all --graph --decorate
* 3be6fd8 (HEAD, master) Correct email address
* 1dcbf21 Merge remote-tracking branch 'p4/master'
|\
| * c4689fc (p4/master, p4/HEAD) Grammar fix
* | cbacd0a Table borders: yes please
* | b4959b6 Trademark
|/
* 775a46f Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
Τα ιστορικά Git και Perforce αποκλίνουν μετά την 775a46f
.
Η πλευρά του Git έχει δύο υποβολές, στη συνέχεια μια συγχώνευση υποβολής με την κεφαλή του Perforce, έπειτα μία άλλη υποβολή.
Θα προσπαθήσουμε να τα υποβάλουμε στην κορυφή ενός ενιαίου συνόλου αλλαγών στην πλευρά του Perforce.
Ας δούμε τι θα συμβεί αν προσπαθήσουμε να υποβάλουμε τώρα:
$ git p4 submit -n
Perforce checkout for depot path //depot/www/live/ located at /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Would synchronize p4 checkout in /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/
Would apply
b4959b6 Trademark
cbacd0a Table borders: yes please
3be6fd8 Correct email address
Η σημαία -n
είναι συνοτομογραφία για το --dry-run
, το οποίο προσπαθεί να αναφέρει τι θα συνέβαινε εάν η εντολή submit
εκτελούνταν πραγματικά.
Σε αυτήν την περίπτωση, φαίνεται ότι θα δημιουργούσαμε τρία σύνολα αλλαγών Perforce, τα οποία θα αντιστοιχίζονταν στις τρεις υποβολές (όχι συγχώνευσης) που δεν υπάρχουν ακόμα στον διακομιστή Perforce.
Αυτό ακούγεται ότι είναι ακριβώς αυτό που θέλουμε, ας δούμε τι έκβαση θα έχει:
$ git p4 submit
[…]
$ git log --oneline --all --graph --decorate
* dadbd89 (HEAD, p4/master, p4/HEAD, master) Correct email address
* 1b79a80 Table borders: yes please
* 0097235 Trademark
* c4689fc Grammar fix
* 775a46f Change page title
* 05f1ade Update link
* 75cd059 Update copyright
* 70eaf78 Initial import of //depot/www/live/ from the state at revision #head
Το ιστορικό μας έγινε γραμμικό, σαν να είχαμε κάνει αλλαγή βάσης πριν υποβάλουμε (και στην πραγματικότητα αυτό ακριβώς συνέβη). Αυτό σημαίνει ότι μπορούμε να δημιουργήσουμε, να εργαστούμε, να πετάξουμε και να συγχωνεύσουμε κλάδους στην πλευρά του Git χωρίς να φοβόμαστε ότι το ιστορικό μας μας θα καταστεί ασυμβίβαστο με το Perforce. Εάν μπορούμε να αλλάξουμε τη βάση του, μπορούμε να το συνεισφέρουμε σε ένα διακομιστή Perforce.
Διακλάδωση
Εάν το έργο Perforce έχει πολλούς κλάδους, δεν είμαστε και τόσο άτυχοι. Η git-p4
μπορεί να το χειριστεί με τρόπο που το δίνει την αίσθηση του Git.
Ας υποθέσουμε ότι η αποθήκη Perforce είναι ως εξής:
//depot
└── project
├── main
└── dev
Και ας πούμε ότι έχουμε έναν κλάδο dev
, ο οποίος έχει μία προβολή spec που μοιάζει με αυτό:
//depot/project/main/... //depot/project/dev/...
Η git-p4
μπορεί να εντοπίσει αυτόματα την κατάσταση και να κάνει το σωστό:
$ git p4 clone --detect-branches //depot/project@all
Importing from //depot/project@all into project
Initialized empty Git repository in /private/tmp/project/.git/
Importing revision 20 (50%)
Importing new branch project/dev
Resuming with change 20
Importing revision 22 (100%)
Updated branches: main dev
$ cd project; git log --oneline --all --graph --decorate
* eae77ae (HEAD, p4/master, p4/HEAD, master) main
| * 10d55fb (p4/project/dev) dev
| * a43cfae Populate //depot/project/main/... //depot/project/dev/....
|/
* 2b83451 Project init
Ας σημειωθεί ο προδιαγραφέας “@all” στη διαδρομή αποθήκευσης· αυτό λέει στην git-p4
να κλωνοποιήσει όχι μόνο το τελευταίο σύνολο αλλαγών για αυτό το υποδέντρο, αλλά όλες τις αλλαγές που έχουν αγγίξει ποτέ αυτές τις διαδρομές.
Αυτό είναι πιο κοντά στην αντίληψη του Git για έναν κλώνο, αλλά αν εργαζόμαστε σε ένα έργο με εκτενές ιστορικό, αυτό μπορεί να έχει μεγάλη διάρκεια.
Η σημαία --detect-branches
λέει στην git-p4
να χρησιμοποιήσει τις προδιαγραφές κλάδων του Perforce για να απεικονίσει τους κλάδους σε ref του Git.
Εάν αυτές οι απεικονίσεις δεν υπάρχουν στον διακομιστή Perforce (και αυτό είναι ένας απόλυτα έγκυρος τρόπος χρήσης του Perforce), μπορούμε να πούμε στην git-p4
ποιες είναι οι απεικονίσεις κλάδων και έχουμε το ίδιο αποτέλεσμα:
$ git init project
Initialized empty Git repository in /tmp/project/.git/
$ cd project
$ git config git-p4.branchList main:dev
$ git clone --detect-branches //depot/project@all .
Η ρύθμιση της μεταβλητής διαμόρφωσης git-p4.branchList
σε main:dev
λέει στην git-p4
ότι οι main
και dev
είναι και οι δύο κλάδοι και ο δεύτερος είναι παιδί του πρώτου.
Αν τώρα κάνουμε git checkout -b dev p4/project/dev
και μετά κάποιες υποβολές, η git-p4
είναι αρκετά έξυπνη ώστε να θεωρήσει ως στόχο τον σωστό κλάδο όταν κάνουμε git p4 submit
.
Δυστυχώς, η git-p4
δεν μπορεί να αναμίξει ρηχούς κλώνους και πολλαπλούς κλάδους· εάν έχουμε ένα τεράστιο έργο και θέλουμε να εργαστούμε σε περισσότερους από έναν κλάδους, θα πρέπει να κάνουμε git p4 clone
μία φορά για κάθε κλάδο στο οποίο θέλουμε να υποβάλουμε.
Για τη δημιουργία ή την ενοποίηση κλάδων, θα πρέπει να χρησιμοποιήσουμε έναν πελάτη Perforce.
Η git-p4
μπορεί μόνο να συγχρονίζει και να υποβάλλει σε υπάρχοντες κλάδους και μάλιστα μπορεί να το κάνει με μόνο μία γραμμική αλλαγή κάθε φορά.
Εάν συγχωνεύσουμε δύο κλάδους στο Git και προσπαθήσουμε να υποβάλουμε τη νέα σειρά αλλαγών, όλα αυτά που θα καταγραφούν είναι κάμποσες αλλαγές αρχείων· τα μεταδεδομένα σχετικά με τους κλάδους που εμπλέκονται στην ενσωμάτωση θα χαθούν.
Ανακεφαλαίωση Git και Perforce
Η git-p4
καθιστά δυνατή τη χρήση μιας ροής εργασίας Git με έναν διακομιστή Perforce και είναι αρκετά καλή σε αυτό.
Ωστόσο, είναι σημαντικό να θυμόμαστε ότι το Perforce είναι υπεύθυνο για την πηγή και χρησιμοποιούμε το Git μόνο για να εργαστούμε τοπικά.
Απλά ας είμαστε πολύ προσεκτικοί σχετικά με την κοινή χρήση των υποβολών του Git· εάν διαθέτουμε έναν απομακρυσμένο κλάδο στον οποίο μπορούν να δουλεύουν άλλοι χρήστες, πρέπει να μην ωθούμε υποβολές που δεν έχουν ήδη υποβληθεί στον διακομιστή Perforce.
Αν θέλουμε να αναμίξουμε ελεύθερα τη χρήση του Perforce και του Git ως πελάτες για τον έλεγχο πηγαίου κώδικα και μπορούμε να πείσουμε τον διαχειριστή του διακομιστή να το εγκαταστήσει, το Git Fusion κάνει χρήση του Git ως πελάτης ελέγχου εκδόσεων πρώτης κατηγορίας για έναν διακομιστή Perforce.
Git and TFS
Το Git γίνεται δημοφιλές στους προγραμματιστές Windows και εάν γράφουμε κώδικα σε Windows, υπάρχει μεγάλη πιθανότητα να χρησιμοποιούμε το Team Foundation Server (TFS) της Microsoft. Το TFS είναι μια σουίτα συνεργασίας που περιλαμβάνει την παρακολούθηση ελαττωμάτων και αντικειμένων εργασίας, υποστήριξη διαδικασιών Scrum και άλλων, αναθεώρηση κώδικα και έλεγχο εκδόσεων. Υπάρχει μια μικρή σύγχυση: TFS είναι ο διακομιστής, ο οποίος υποστηρίζει τον έλεγχο του πηγαίου κώδικα χρησιμοποιώντας τόσο το Git όσο και το δικό του προσαρμοσμένο VCS, το οποίο έχουν ονομάσει TFVC (Team Foundation Version Control). Η υποστήριξη για Git είναι μια κάπως νέα δυνατότητα για το TFS (από την έκδοση 2013), έτσι όλα τα εργαλεία που προηγούνται αυτής της έκδοσης αναφέρονται στο κομμάτι του ελέγχου εκδόσεων ως “TFS”, αν και εργάζονται κυρίως με το TFVC.
Αν βρεθούμε σε μια ομάδα που χρησιμοποιεί το TFVC αλλά θα προτιμούσαμε να χρησιμοποιούμε το Git ως τον πελάτη ελέγχου εκδόσεων, υπάρχει ένα έργο για εμάς.
Ποιο εργαλείο;
Στην πραγματικότητα, υπάρχουν δύο: τα git-tf και git-tfs.
Το git-tfs
(που βρίσκεται στη https://github.com/git-tfs/git-tfs) είναι ένα έργο .NET και (τουλάχιστον τώρα που γράφεται αυτό το κείμενο) τρέχει μόνο σε Windows.
Για να συνεργαστεί με αποθετήρια Git, χρησιμοποιεί συνδέσεις της .NET για libgit2, μια εφαρμογή του Git που προσανατολίζεται στη βιβλιοθήκη, η οποία είναι εξαιρετικά αποδοτική και επιτρέπει μεγάλη ευελιξία με τις εσωτερικές λειτουργίες ενός αποθετηρίου Git.
Το Libgit2 δεν είναι μια ολοκληρωμένη εφαρμογή του Git, οπότε για να καλύψει τη διαφορά, το git-tfs καλεί τη γραμμής εντολών του πελάτη Git για κάποιες λειτουργίες κι έτσι δεν υπάρχουν τεχνητά όρια στο τι μπορεί να κάνει με τα αποθετήρια Git.
Η υποστήριξη των χαρακτηριστικών του TFVC είναι πολύ ώριμη, αφού χρησιμοποιεί τα συγκροτήσεις Visual Studio για εργασίες με διακομιστές.
Αυτό σημαίνει ότι θα χρειαστούμε πρόσβαση σε αυτές τις συγκροτήσεις, πράγμα που σημαίνει ότι πρέπει να εγκαταστήσουμε μια πρόσφατη έκδοση του Visual Studio (οποιαδήποτε έκδοση από την έκδοση 2010 και μετά, συμπεριλαμβανομένης της Express από την έκδοση 2012) ή το Visual Studio SDK.
Το git-tf
(του οποίου το σπίτι βρίσκεται στη https://gittf.codeplex.com) είναι ένα έργο Java και ως εκ τούτου τρέχει σε οποιονδήποτε υπολογιστή με περιβάλλον εκτέλεσης Java (Java runtime environment).
Συνδέεται με τα αποθετήρια Git μέσω του JGit (εφαρμογή JVM του Git), πράγμα που σημαίνει ότι δεν έχει ουσιαστικά κανένα περιορισμό όσον αφορά τις λειτουργίες του Git.
Ωστόσο, η υποστήριξή του για το TFVC είναι περιορισμένη σε σύγκριση με το git-tfs —για παράδειγμα, δεν υποστηρίζει κλάδους.
Έτσι κάθε εργαλείο έχει πλεονεκτήματα και μειονεκτήματα και υπάρχουν πολλές καταστάσεις που ευνοούν το ένα σε σχέση με το άλλο. Σε αυτό το βιβλίο Θα καλύψουμε τη βασική χρήση και των δύο.
Note
|
Θα χρειαστούμε πρόσβαση σε ένα αποθετήριο TFVC για να παρακολουθήσουμε αυτά τα παραδείγματα. Αυτά δεν είναι τόσο άφθονα εκεί έξω όσο τα αποθετήρια Git ή Subversion, οπότε μπορεί να χρειαστεί να δημιουργήσουμε ένα δικό μας. Το Codeplex (https://www.codeplex.com) ή το Visual Studio Online (http://www.visualstudio.com) είναι και οι δύο καλές επιλογές για κάτι τέτοιο. |
Ξεκινώντας με το git-tf
Το πρώτο πράγμα που κάνουμε, όπως συμβαίνει με οποιοδήποτε πρόγραμμα Git, είναι ο κλώνος.
Με το git-tf
αυτό μοιάζει ως εξής:
$ git tf clone https://tfs.codeplex.com:443/tfs/TFS13 $/myproject/Main project_git
Το πρώτο όρισμα είναι η διεύθυνση URL μιας συλλογής TFVC, η δεύτερη είναι της μορφής $/έργο/κλάδος
και η τρίτη είναι η διαδρομή προς τον τοπικό αποθετήριο Git που πρόκειται να δημιουργηθεί (το τελευταίο είναι προαιρετικό).
Το git-tf
μπορεί να λειτουργήσει μόνο με έναν κλάδο κάθε φορά· εάν θέλουμε να κάνουμε checkin
σε διαφορετικό κλάδο TFVC, θα πρέπει να κάνουμε έναν νέο κλώνο από αυτόν τον κλάδο.
Αυτό δημιουργεί ένα πλήρως λειτουργικό αποθετήριο Git:
$ cd project_git
$ git log --all --oneline --decorate
512e75a (HEAD, tag: TFS_C35190, origin_tfs/tfs, master) Checkin message
Αυτό ονομάζεται ρηχός κλώνος, πράγμα που σημαίνει ότι έχει ληφθεί μόνο το τελευταίο σύνολο αλλαγών. Το TFVC δεν έχει σχεδιαστεί έτσι ώστε κάθε πελάτης να έχει ένα πλήρες αντίγραφο του ιστορικού, οπότε το git-tf έχει ως προεπιλογή να παίρνει μόνο την πιο πρόσφατη έκδοση, που είναι και το πιο γρήγορο.
Αν έχουμε κάποιο χρόνο, αξίζει τον κόπο να κλωνοποιήσουμε ολόκληρο το ιστορικό του έργου, χρησιμοποιώντας την επιλογή --deep
:
$ git tf clone https://tfs.codeplex.com:443/tfs/TFS13 $/myproject/Main \
project_git --deep
Username: domain\user
Password:
Connecting to TFS...
Cloning $/myproject into /tmp/project_git: 100%, done.
Cloned 4 changesets. Cloned last changeset 35190 as d44b17a
$ cd project_git
$ git log --all --oneline --decorate
d44b17a (HEAD, tag: TFS_C35190, origin_tfs/tfs, master) Goodbye
126aa7b (tag: TFS_C35189)
8f77431 (tag: TFS_C35178) FIRST
0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
Παρατηρούμε ετικέτες με ονόματα όπως TFS_C35189
· αυτό είναι ένα χαρακτηριστικό που μας βοηθάει να ξέρουμε ποιες υποβολές του Git σχετίζονται με το σύνολο αλλαγών του TFVC.
Αυτός είναι ένας καλός τρόπος αναπαράστασης, αφού μπορούμε να δούμε με μια απλή εντολή log
ποια από τις υποβολές μας σχετίζεται με ένα στιγμιότυπο που υπάρχει επίσης στο TFVC.
Δεν είναι απαραίτητες (στην πραγματικότητα μπορούμε να τις απενεργοποιήσουμε με την git config git-tf.tag false
) —η git-tf
διατηρεί τις πραγματικές αντιστοιχίσεις υποβολών-συνόλων αλλαγών στο αρχείο .git/git-tf
.
Ξεκινώντας με το git-tfs
Η κλωνοποίηση του git-tfs
συμπεριφέρεται λίγο διαφορετικά:
PS> git tfs clone --with-branches \
https://username.visualstudio.com/DefaultCollection \
$/project/Trunk project_git
Initialized empty Git repository in C:/Users/ben/project_git/.git/
C15 = b75da1aba1ffb359d00e85c52acb261e4586b0c9
C16 = c403405f4989d73a2c3c119e79021cb2104ce44a
Tfs branches found:
- $/tfvc-test/featureA
The name of the local branch will be : featureA
C17 = d202b53f67bde32171d5078968c644e562f1c439
C18 = 44cd729d8df868a8be20438fdeeefb961958b674
Παρατηρούμε τη σημαία --with-branches
.
Το git-tfs
είναι ικανό να απεικονίσει κλάδους TFVC σε κλάδους Git και αυτή η σημαία του λέει να δημιουργήσει έναν τοπικό κλάδο Git για κάθε κλάδο TFVC.
Αυτό συνιστάται ιδιαίτερα αν έχουμε διακλαδιστεί ή συγχωνευτεί στο TFS, αλλά δεν θα λειτουργήσει με διακομιστές παλαιότερους από τον TFS 2010 —πριν από αυτήν την έκδοση, οι “κλάδοι” ήταν απλά κατάλογοι, οπότε το git-tfs
δεν μπορεί να τους ξεχωρίσει από τους κανονικούς καταλόγους.
Ας ρίξουμε μια ματιά στο αποθετήριο Git που προκύπτει:
PS> git log --oneline --graph --decorate --all
* 44cd729 (tfs/featureA, featureA) Goodbye
* d202b53 Branched from $/tfvc-test/Trunk
* c403405 (HEAD, tfs/default, master) Hello
* b75da1a New project
PS> git log -1
commit c403405f4989d73a2c3c119e79021cb2104ce44a
Author: Ben Straub <ben@straub.cc>
Date: Fri Aug 1 03:41:59 2014 +0000
Hello
git-tfs-id: [https://username.visualstudio.com/DefaultCollection]$/myproject/Trunk;C16
Υπάρχουν δύο τοπικοί κλάδοι, master
και featureA
, που αναπαριστούν το αρχικό σημείο εκκίνησης του κλώνου (Trunk
στο TFVC) και έναν κλάδο-απόγονο (featureA
στο TFVC).
Μπορούμε επίσης να δούμε ότι το “απομακρυσμένο αποθετήριο” του tfs
έχει επίσης μερικά refs: default
και featureA
, που αναπαριστούν κλάδους του TFVC.
Το git-tfs
απεικονίζει τον κλάδο που έχουμε κλωνοποιήσει από το tfs/default
, και οι άλλοι παίρνουν τα δικά τους ονόματα.
Ένα άλλο πράγμα που πρέπει να παρατηρήσουμε είναι οι γραμμές git-tfs-id:
στα μηνύματα υποβολών.
Αντί των ετικετών, το git-tfs
χρησιμοποιεί αυτά τα σημάδια για να συνδέσει τα σύνολα αλλαγών του TFVC με τις υποβολές Git.
Αυτό έχει ως συνέπεια ότι οι υποβολές μας θα έχουν διαφορετικό αριθμο SHA-1 πριν και μετά την ώθησή τους στο TFVC.
Ροή εργασίας των git-tf
και git-tfs
Note
|
Ανεξάρτητα από το εργαλείο που χρησιμοποιούμε, θα πρέπει να ορίσουμε μερικές τιμές διαμόρφωσης του Git για να αποφύγουμε την εμφάνιση προβλημάτων.
|
Το προφανές επόμενο πράγμα που θα θελήσουμε να κάνουμε είναι να εργαστούμε στο έργο. Το TFVC και το TFS έχουν διάφορα χαρακτηριστικά που μπορεί να προσθέτουν πολυπλοκότητα στη ροή εργασίας μας:
-
Οι κλάδοι χαρακτηριστικών που δεν αναπαριστώνται στο TFVC προσθέτουν μια μικρή πολυπλοκότητα. Αυτό έχει να κάνει με τον πολύ διαφορετικό τρόπο με τον οποίο οι TFVC και Git αναπαριστούν κλάδους.
-
Ύπόψη ότι το TFVC επιτρέπει στους χρήστες να ανακτήσουν τα αρχεία από τον διακομιστή, κλειδώνοντάς τα έτσι ώστε κανένας άλλος δεν μπορεί να τα επεξεργάζεται. Αυτό προφανώς δεν θα μας εμποδίσει να τα επεξεργαστούμε στο τοπικό αποθετήριό μας, αλλά θα μπορούσε να μας δυσκολέψει όταν έρχεται η ώρα να ωθήσουμε τις αλλαγές μας στον διακομιστή TFVC.
-
Το TFS έχει την έννοια των “φραγμένων”
checkin
, όπου ένας κύκλος δοκιμής TFS πρέπει να ολοκληρωθεί με επιτυχία πριν επιτραπεί ηcheckin
. Αυτό χρησιμοποιεί τη λειτουργία “shelve” του TFVC, την οποία δεν καλύπτουμε λεπτομερώς εδώ. Μπορούμε να προσποιηθούμε με χειροκίνητο τρόπο με τοgit-tf
και τοgit-tfs
παρέχει την εντολήcheckinto
που έχει επίγνωση της φραγής.
Χάρη συντομίας, αυτό που θα καλύψουμε εδώ είναι το βατό μονοπάτι, το οποίο παρακάμπτει ή αποφεύγει τα περισσότερα από αυτά τα προβλήματα.
Ροή εργασίας: git-tf
Ας υποθέσουμε ότι έχουμε κάνει κάποια εργασία, κάναμε μερικές υποβολές του Git στον master
και είμαστε έτοιμοι να μοιραστούμε την πρόοδό μας στον διακομιστή TFVC.
Εδώ είναι το αποθετήριο Git:
$ git log --oneline --graph --decorate --all
* 4178a82 (HEAD, master) update code
* 9df2ae3 update readme
* d44b17a (tag: TFS_C35190, origin_tfs/tfs) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
Θέλουμε να πάρουμε το στιγμιότυπο που βρίσκεται στην 4178a82
και να το ωθήσουμε στον διακομιστή TFVC.
Καταρχάς ας δούμε αν οποιοσδήποτε από τους συμπαίκτες μας έκανε κάτι από τότε που συνδεθήκαμε τελευταία φορά:
$ git tf fetch
Username: domain\user
Password:
Connecting to TFS...
Fetching $/myproject at latest changeset: 100%, done.
Downloaded changeset 35320 as commit 8ef06a8. Updated FETCH_HEAD.
$ git log --oneline --graph --decorate --all
* 8ef06a8 (tag: TFS_C35320, origin_tfs/tfs) just some text
| * 4178a82 (HEAD, master) update code
| * 9df2ae3 update readme
|/
* d44b17a (tag: TFS_C35190) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
Φαίνεται ότι κάποιος άλλος εργάζεται επίσης και συνεπώς τώρα έχουμε αποκλίνον ιστορικό. Εδώ είναι που λάμπει το Git λάμπει, αλλά έχουμε δύο επιλογές για το πώς θα προχωρήσουμε:
-
Μία υποβολή συγχώνευσης φαίνεται λογική ως χρήστης του Git (άλλωστε αυτό είναι που κάνει η
git pull
) και ηgit-tf
μπορεί να το κάνει αυτό για μας με ένα απλόgit tf pull
. Ας έχουμε υπόψη, ωστόσο, ότι το TFVC δεν σκέφτεται με αυτόν τον τρόπο και αν ωθήσουμε υποβολές συγχώνευσης, το ιστορικό μας θα αρχίσει να φαίνεται διαφορετικό στις δύο πλευρές, κάτι που μπορεί να προκαλέσει σύγχυση. Αν σχεδιάζουμε να υποβάλουμε όλες τις αλλαγές μας ως ένα σύνολο αλλαγών, αυτή είναι ίσως η πιο εύκολη επιλογή. -
Η αλλαγή βάσης καθιστά το ιστορικό υποβολών μας γραμμικό, πράγμα που σημαίνει ότι έχουμε τη δυνατότητα να μετατρέψουμε καθεμία από τις υποβολές μας Git σε ένα σύνολο αλλαγών του TFVC. Δεδομένου ότι αυτό αφήνει τις περισσότερες επιλογές ανοικτές, συνιστάται να το κάνουμε με αυτόν τον τρόπο. Το
git-tf
μας διευκολύνει ακόμα και με τηνgit tf pull --rebase
.
Η επιλογή είναι δική μας. Σε αυτό το παράδειγμα, θα κάνουμε αλλαγή βάσης:
$ git rebase FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: update readme
Applying: update code
$ git log --oneline --graph --decorate --all
* 5a0e25e (HEAD, master) update code
* 6eb3eb5 update readme
* 8ef06a8 (tag: TFS_C35320, origin_tfs/tfs) just some text
* d44b17a (tag: TFS_C35190) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
Τώρα είμαστε έτοιμοι να κάνουμε checkin
στον διακομιστή TFVC.
Το git-tf
μας δίνει τις εξής επιλογές: να δημιουργήσουμε ένα ενιαίο σύνολο αλλαγών που αναπαριστά όλες τις αλλαγές από την τελευταία checkin
(--shallow
, που είναι και η προεπιλογή) ή να δημιουργήσουμε ένα νέο σύνολο αλλαγών για κάθε υποβολή Git (--deep
).
Σε αυτό το παράδειγμα, θα δημιουργήσουμε μόνο ένα σύνολο αλλαγών:
$ git tf checkin -m 'Updating readme and code'
Username: domain\user
Password:
Connecting to TFS...
Checking in to $/myproject: 100%, done.
Checked commit 5a0e25e in as changeset 35348
$ git log --oneline --graph --decorate --all
* 5a0e25e (HEAD, tag: TFS_C35348, origin_tfs/tfs, master) update code
* 6eb3eb5 update readme
* 8ef06a8 (tag: TFS_C35320) just some text
* d44b17a (tag: TFS_C35190) Goodbye
* 126aa7b (tag: TFS_C35189)
* 8f77431 (tag: TFS_C35178) FIRST
* 0745a25 (tag: TFS_C35177) Created team project folder $/tfvctest via the \
Team Project Creation Wizard
Υπάρχει μια νέα ετικέτα TFS_C35348
, που υποδεικνύει ότι το TFVC αποθηκεύει ακριβώς το ίδιο στιγμιότυπο με την υποβολή 5a0e25e
.
Είναι σημαντικό να σημειώσουμε ότι δεν είναι απαραίτητο κάθε υποβολή Git να έχει ένα ακριβές αντίγραφο στο TFVC· η υποβολή 6eb3eb5
, για παράδειγμα, δεν υπάρχει σε κανένα σημείο του διακομιστή.
Αυτή είναι η κύρια ροή εργασίας. Υπάρχουν μερικά άλλα θέματα που πρέπει να έχουμε κατά νου:
-
Δεν υπάρχει διακλάδωση. Το
git-tf
μπορεί να δημιουργήσει μόνο αποθετήρια Git από έναν κλάδο TFVC κάθε φορά. -
Συνεργαζόμαστε χρησιμοποιώντας είτε TFVC είτε Git αλλά όχι και τα δύο. Διαφορετικοί κλώνοι
git-tf
του ίδιου αποθετηρίου TFVC μπορεί να έχουν διαφορετικούς SHA-1 υποβολής, οι οποίοι θα μας προκαλέσουν ατελείωτους πονοκεφάλους. -
Αν η ροή εργασίας της ομάδας μας περιλαμβάνει συνεργασία με το Git και συγχρονισμό με το TFVC περιοδικά, συνδεόμαστε μόνο στο TFVC μόνο με ένα από τα αποθετήρια Git.
Ροή εργασίας: git-tfs
Ας περάσουμε από το ίδιο σενάριο χρησιμοποιώντας το git-tfs
.
Ακολουθούν οι νέες υποβολές που έχουμε κάνει στον κλάδο master
στο αποθετήριο Git:
PS> git log --oneline --graph --all --decorate
* c3bd3ae (HEAD, master) update code
* d85e5a2 update readme
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 (tfs/default) Hello
* b75da1a New project
Τώρα ας δούμε αν κάποιος άλλος έχει κάνει δουλειά ενώ εμείς κάναμε τις δικές μας αλλαγές:
PS> git tfs fetch
C19 = aea74a0313de0a391940c999e51c5c15c381d91d
PS> git log --all --oneline --graph --decorate
* aea74a0 (tfs/default) update documentation
| * c3bd3ae (HEAD, master) update code
| * d85e5a2 update readme
|/
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
Ναι, αποδεικνύεται ότι ο συνεργάτης μας έχει προσθέσει ένα νέο σύνολο αλλαγών TFVC, το οποίο εμφανίζεται με τη νέα υποβολή aea74a0
και ο απομακρυσμένος κλάδος tfs/default
μετακινήθηκε.
Όπως και με το git-tf
, έχουμε δύο βασικές επιλογές για τον τρόπο επίλυσης αυτού του αποκλίνοντος ιστορικού:
-
Αλλαγή βάσης για να διατηρήσουμε το ιστορικό γραμμικό.
-
Συγχώνευση για να διατηρήσουμε αυτό που πραγματικά συνέβη.
Σε αυτήν την περίπτωση, θα κάνουμε ένα “βαθύ” checkin
, στο οποίο κάθε υποβολή Git θα γίνει ένα σύνολο αλλαγών TFVC, οπότε θέλουμε να αλλάξουμε τη βάση.
PS> git rebase tfs/default
First, rewinding head to replay your work on top of it...
Applying: update readme
Applying: update code
PS> git log --all --oneline --graph --decorate
* 10a75ac (HEAD, master) update code
* 5cec4ab update readme
* aea74a0 (tfs/default) update documentation
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
Τώρα είμαστε έτοιμοι να ολοκληρώσουμε τη συνεισφορά μας ελέγχοντας τον κώδικά μας στον διακομιστή TFVC.
Θα χρησιμοποιήσουμε την εντολή rcheckin
για να δημιουργήσουμε ένα σύνολο αλλαγών TFVC για κάθε υποβολή Git στη διαδρομή από τον HEAD στον πρώτο απομακρυσμένο κλάδο tfs
που βρέθηκε (η εντολή checkin
θα δημιουργούσε μόνο ένα σύνολο αλλαγών, όπως η συναρμογή υποβολών στο Git).
PS> git tfs rcheckin
Working with tfs remote: default
Fetching changes from TFS to minimize possibility of late conflict...
Starting checkin of 5cec4ab4 'update readme'
add README.md
C20 = 71a5ddce274c19f8fdc322b4f165d93d89121017
Done with 5cec4ab4b213c354341f66c80cd650ab98dcf1ed, rebasing tail onto new TFS-commit...
Rebase done successfully.
Starting checkin of b1bf0f99 'update code'
edit .git\tfs\default\workspace\ConsoleApplication1/ConsoleApplication1/Program.cs
C21 = ff04e7c35dfbe6a8f94e782bf5e0031cee8d103b
Done with b1bf0f9977b2d48bad611ed4a03d3738df05ea5d, rebasing tail onto new TFS-commit...
Rebase done successfully.
No more to rcheckin.
PS> git log --all --oneline --graph --decorate
* ff04e7c (HEAD, tfs/default, master) update code
* 71a5ddc update readme
* aea74a0 update documentation
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
Παρατηρούμε πως μετά από κάθε επιτυχές checkin
στον διακομιστή TFVC, το git-tfs
αλλάζει τη βάση του υπόλοιπου έργου σε αυτό που μόλις έκανε.
Αυτό συμβαίνει επειδή προσθέτει το πεδίο git-tfs-id
στο κάτω μέρος των μηνυμάτων υποβολής, το οποίο αλλάζει τους αριθμούς SHA-1.
Αυτό είναι ακριβώς όπως έχει σχεδιαστεί και δεν υπάρχει κάτι να μας ανησυχεί, αλλά θα πρέπει να γνωρίζουμε ότι συμβαίνει αυτό, ειδικά αν μοιραζόμαστε υποβολές Git με άλλους.
Το TFS έχει πολλές λειτουργίες που ενσωματώνονται με το σύστημα ελέγχου εκδόσεών του, όπως στοιχεία εργασίας, ορισθέντες αναθεωρητές, φραγμένα checkin κ.ο.κ.
Η εργασία με αυτά τα χαρακτηριστικά χρησιμοποιώντας μόνο το εργαλείο γραμμής εντολών μπορεί να είναι δύσχρηστη, αλλά ευτυχώς το git-tfs μας επιτρέπει να ξεκινήσουμε ένα γραφικό εργαλείο checkin (checkintool
) πολύ εύκολα:
PS> git tfs checkintool
PS> git tfs ct
Μοιάζει λίγο σαν αυτό:

git-tfs checkintool
.Αυτό είναι οικείο στους χρήστες του TFS, καθώς είναι ο ίδιος διάλογος που ξεκινάει μέσα από το Visual Studio.
Το git-tfs
μας επιτρέπει επίσης να ελέγχουμε τους κλάδους του TFVC από το αποθετήριο Git.
Για παράδειγμα, ας δημιουργήσουμε ένα:
PS> git tfs branch $/tfvc-test/featureBee
The name of the local branch will be : featureBee
C26 = 1d54865c397608c004a2cadce7296f5edc22a7e5
PS> git log --oneline --graph --decorate --all
* 1d54865 (tfs/featureBee) Creation branch $/myproject/featureBee
* ff04e7c (HEAD, tfs/default, master) update code
* 71a5ddc update readme
* aea74a0 update documentation
| * 44cd729 (tfs/featureA, featureA) Goodbye
| * d202b53 Branched from $/tfvc-test/Trunk
|/
* c403405 Hello
* b75da1a New project
Η δημιουργία ενός κλάδου στο TFVC σημαίνει την προσθήκη ενός συνόλου αλλαγών όπου αυτό υπάρχει αυτός ο κλάδος αυτήν τη στιγμή και αυτό προβάλλεται ως υποβολή Git.
Ας σημειωθεί επίσης ότι το git-tfs
δημιούργησε τον απομακρυσμένο κλάδο tfs/featureBee
, αλλά ο HEAD
εξακολουθεί να δείχνει στον master
.
Εάν θέλουμε να εργαστούμε στον νεοσύστατο κλάδο, θα θελήσουμε να βασίσουμε τις νέες υποβολές μας στην υποβολή 1d54865
, ίσως δημιουργώντας έναν θεματικό κλάδο από αυτήν την υποβολή.
Ανακεφαλαίωση Git και TFS
Τα git-tf
και git-tfs
είναι και τα δύο εξαιρετικά εργαλεία για τη διασύνδεση με έναν διακομιστή TFVC.
Μας επιτρέπουν να χρησιμοποιούμε τη δύναμη του Git τοπικά, να αποφεύγουμε να ταξιδεύουμε συνεχώς στον κεντρικό διακομιστή TFVC και να κάνουν τη ζωή μας ως προγραμματιστών πολύ πιο εύκολη, χωρίς να αναγκάζουν ολόκληρη την ομάδα μας να μεταναστεύσει στο Git.
Εάν εργαζόμαστε σε Windows (που είναι και το πιο πιθανό, εφόσον η ομάδα μας χρησιμοποιεί το TFS), πιθανότατα θέλουμε να χρησιμοποιούμε το git-tfs
, καθώς το σύνολο χαρακτηριστικών του είναι πιο πλήρες, αλλά αν εργαζόμαστε σε άλλη πλατφόρμα, θα θέλαμε να χρησιμοποιούμε το Γιτ-tf, το οποίο είναι πιο περιορισμένο.
Όπως συμβαίνει με τα περισσότερα εργαλεία αυτού του κεφαλαίου, θα πρέπει να επιλέξουμε ένα από αυτά τα συστήματα ελέγχου εκδόσεων να είναι κανονικό (canonical) και να χρησιμοποιήσουμε το άλλο με δευτερεύοντα τρόπο —είτε το Git είτε το TFVC θα πρέπει να είναι το κέντρο συνεργασίας, αλλά όχι και τα δύο.