Git
Chapters ▾ 2nd Edition

10.3 Εσωτερική λειτουργία του Git - Αναφορές του Git

Αναφορές του Git

Μπορούμε να εκτελέσουμε κάτι σαν το git log 1a410e για να δούμε όλο το ιστορικό μας, αλλά πρέπει ακόμα να θυμόμαστε ότι η 1a410e είναι η τελευταία υποβολή, για να διασχίσουμε εκείνο το ιστορικό και να βρούμε όλα αυτά τα αντικείμενα. Χρειαζόμαστε ένα αρχείο στο οποίο μπορούμε να αποθηκεύσουμε την τιμή SHA-1 με ένα απλό όνομα, ώστε να μπορούμε να χρησιμοποιήσουμε αυτόν τον δείκτη και όχι την τιμή SHA-1.

Στο Git, αυτά ονομάζονται “αναφορές” (references) ή “refs”· μπορούμε να βρούμε τα αρχεία που περιέχουν τις τιμές SHA-1 στον κατάλογο .git/refs. Στο τρέχον έργο, αυτός ο κατάλογος δεν περιέχει αρχεία, αλλά περιέχει μια απλή δομή:

$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f

Για να δημιουργήσουμε μια νέα αναφορά που θα μας βοηθήσει να θυμόμαστε πού είναι η τελευταία μας υποβολή, τεχνικά μπορούμε να κάνουμε κάτι τόσο απλό όπως το εξής:

$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master

Τώρα μπορούμε να χρησιμοποιήσουμε την αναφορά κεφαλής που μόλις δημιουργήσαμε αντί της τιμής SHA-1 στις εντολές Git:

$ git log --pretty=oneline  master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

Καλό θα είναι να μην επεξεργαζόμαστε απευθείας τα αρχεία αναφορών. Το Git παρέχει μία ασφαλέστερη εντολή εάν θέλουμε να ενημερώσουμε μια αναφορά, την update-ref,:

$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9

Αυτό βασικά είναι τι είναι ένας κλάδος στο Git: ένας απλός δείκτης ή μια αναφορά στην κεφαλή μιας γραμμής εργασίας. Για να δημιουργήσουμε έναν κλάδο πίσω στη δεύτερη υποβολή, μπορούμε να το κάνουμε το εξής:

$ git update-ref refs/heads/test cac0ca

Ο κλάδος μας θα περιέχει μόνο εργασίες από αυτήν την υποβολή και μετά:

$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

Τώρα, η βάση δεδομένων του Git μοιάζει με αυτό:

Αντικείμενα του καταλόγου του Git συμπεριλαμβάνομένων αναφορών κεφαλής κλάδου.
Figure 151. Αντικείμενα του καταλόγου του Git συμπεριλαμβάνομένων αναφορών κεφαλής κλάδου.

Όταν εκτελούμε εντολές όπως η git branch <όνομα_κλάδου>, το Git βασικά τρέχει αυτήν την εντολή update-ref για να προσθέσει τον αριθμό SHA-1 της τελευταίας υποβολής του κλάδου στον οποίο βρισκόμαστε σε οποιαδήποτε νέα αναφορά θέλουμε να δημιουργήσουμε.

Ο HEAD

Το ερώτημα τώρα είναι, όταν τρέχουμε την `git branch <όνομα_κλάδου>, πώς γνωρίζει το Git τον αριθμό SHA-1 της τελευταίας υποβολής; Η απάντηση είναι το αρχείο HEAD.

Το αρχείο HEAD είναι συμβολική αναφορά στον κλάδο στο οποίο βρισκόμαστε. Με συμβολική αναφορά, εννοούμε ότι σε αντίθεση με μια κανονική αναφορά, δεν περιέχει γενικά μια τιμή SHA-1, αλλά έναν δείκτη σε μια άλλη αναφορά. Εάν κοιτάξουμε το αρχείο, θα δούμε κάτι τέτοιο:

$ cat .git/HEAD
ref: refs/heads/master

Αν εκτελέσουμε την git checkout test, το Git ενημερώνει το αρχείο ώστε να φαίνεται ως εξής:

$ cat .git/HEAD
ref: refs/heads/test

Όταν εκτελούμε την git commit, δημιουργεί το αντικείμενο υποβολής, ορίζοντας τον γονέα του αντικειμένου υποβολής να είναι όποια τιμή SHA-1 δείχνει η αναφορά στον HEAD.

Μπορούμε επίσης να επεξεργαστούμε χειροκίνητα αυτό το αρχείο, αλλά και πάλι υπάρχει μια ασφαλέστερη εντολή: symbolic-ref. Μπορούμε να διαβάσουμε την τιμή του HEAD μας μέσω αυτής της εντολής:

$ git symbolic-ref HEAD
refs/heads/master

Μπορούμε επίσης να ορίσουμε την τιμή του HEAD:

$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test

Δεν μπορούμε να ορίσουμε μια συμβολική αναφορά εκτός του καταλόγου refs:

$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/

Ετικέτες

Μόλις ολοκληρώσαμε τη συζήτηση για τους τρεις κύριους τύπους αντικειμένων του Git, αλλά υπάρχει ένας τέταρτος. Το αντικείμενο “ετικέτα” μοιάζει πολύ με ένα αντικείμενο υποβολής —περιέχει μία ταμπέλα, μια ημερομηνία, ένα μήνυμα και έναν δείκτη. Η κύρια διαφορά είναι ότι ένα αντικείμενο ετικέτας γενικά οδηγεί σε υποβολή και όχι σε δέντρο. Είναι σαν αναφορά κλάδου, αλλά δεν μετακινείται ποτέ —δείχνει πάντοτε την ίδια υποβολή, αλλά δίνει ένα πιο φιλικό όνομα.

Όπως αναλύθηκε στην ενότητα Τα θεμελιώδη στοιχεία του Git, υπάρχουν δύο τύποι ετικετών: επισημειωμένοι (annotated) και ελαφριές (lightweight). Μπορούμε να κάνουμε μια ελαφριά ετικέτα τρέχοντας κάτι σαν αυτό:

$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d

Μία ελαφριά ετικέτα είναι ακριβώς και μόνον αυτό —μια αναφορά που δεν κινείται ποτέ. Ωστόσο, μια επισημειωμένη ετικέτα είναι πιο περίπλοκη. Αν δημιουργήσουμε μια επισημειωμένη ετικέτα, το Git δημιουργεί ένα αντικείμενο ετικέτας και στη συνέχεια γράφει μια αναφορά που δείχνει σε αυτήν και όχι απευθείας στην υποβολή. Μπορούμε να το δούμε αυτό δημιουργώντας μια επισημειωμένη ετικέτα (η επιλογή -a καθορίζει ότι είναι μια ετικέτα σχολιασμένη):

$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'

Εδώ είναι το αντικείμενο SHA-1 που δημιούργησε:

$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2

Τώρα, ας εκτελέσουμε την εντολή cat-file στην τιμή SHA-1:

$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700

test tag

Παρατηρούμε ότι το αντικείμενο δείχνει στην τιμή SHA-1 της υποβολής που έχουμε επισημάνει. Επίσης παρατηρούμε ότι δεν είναι απαραίτητο να δείχνει σε μια υποβολή· μπορούμε να βάλουμε ετικέτα σε οποιοδήποτε αντικείμενο Git. Στον πηγαίο κώδικα Git, για παράδειγμα, ο συντηρητής έχει προσθέσει το δημόσιο κλειδί GPG ως αντικείμενο blob και έπειτα του προσάρτησε μία ετικέτα. Μπορούμε να δούμε το δημόσιο κλειδί τρέχοντας αυτό σε έναν κλώνο του αποθετηρίου Git:

$ git cat-file blob junio-gpg-pub

Το αποθετήριο του πυρήνα του Linux διαθέτει επίσης ένα αντικείμενο ετικέτας που δεν δείχνει σε υποβολή —η πρώτη ετικέτα που δημιουργήθηκε δείχνει στο αρχικό δέντρο της εισαγωγής του πηγαίου κώδικα.

Απομακρυσμένες αναφορές

Ο τρίτος τύπος αναφοράς που θα δούμε είναι μια απομακρυσμένη αναφορά. Εάν προσθέσουμε ένα απομακρυσμένο αποθετήριο και ωθήσουμε σε αυτό, το Git αποθηκεύει την τιμή που ωθήσαμε τελευταία σε αυτό το απομακρυσμένο αποθετήριο για κάθε κλάδο στον κατάλογο refs/remotes. Για παράδειγμα, μπορούμε να προσθέσουμε ένα απομακρυσμένο αποθετήριο που ονομάζεται origin και να ωθήσουμε τον κλάδο μας master σε αυτό:

$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
  a11bef0..ca82a6d  master -> master

Στη συνέχεια, μπορούμε να δούμε πώς ήταν ο κλάδος master στο απομακρυσμένο αποθετήριο origin την τελευταία φορά που επικοινωνήσαμε με τον διακομιστή, ελέγχοντας το αρχείο refs/remotes/origin/master:

$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949

Οι απομακρυσμένες αναφορές διαφέρουν από τους κλάδους (αναφορές refs/heads) κυρίως επειδή θεωρούνται μόνο για ανάγνωση. Μπορούμε να κάνουμε git checkout σε μία απομακρυσμένη αναφορά, αλλά το Git ποτέ δεν θα κάνει τον HEAD σε μία, συνεπώς ποτέ δεν θα την ενημερώσουμε με μια εντολή commit. Το Git τις διαχειρίζεται ως σελιδοδείκτες στην τελευταία γνωστή κατάσταση στην οποία βρίσκονταν εκείνοι οι κλάδοι σε αυτούς τους διακομιστές.