Git
Chapters ▾ 2nd Edition

7.1 Εργαλεία του Git - Επιλογή αναθεώρησης

Μέχρι τώρα έχουμε μάθει τις περισσότερες από τις καθημερινές εντολές και ροές εργασίας που χρειαζόμαστε για να διαχειριστούμε ή να διαχειριστούμε ένα αποθετήριο Git για τον έλεγχο εκδόσεων του πηγαίου κώδικά μας. Έχουμε φέρει σε πέρας τις θεμελιώδεις εργασίες της παρακολούθησης και υποβολής αρχείων και έχουμε δαμάσει τη δύναμη της ενδιάμεσης περιοχής και τις ελαφριές εργασίες της θεματικής διακλάδωσης και συγχώνευσης.

Τώρα θα εξερευνήσουμε μερικές πολύ ισχυρές λειτουργίες που μπορεί να κάνει το Git που δεν θα χρησιμοποιήσουμε απαραίτητα σε καθημερινή βάση αλλά ενδεχομένως θα χρειαστούμε σε κάποια στιγμή.

Επιλογή αναθεώρησης

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

Απλές αναθεωρήσεις

Μπορούμε προφανώς να αναφερθούμε σε μια υποβολή από τον αριθμό SHA-1 που δίνεται αλλά υπάρχουν πιο φιλικοί-προς-τον-άνθρωπο τρόποι για να αναφερθεί κανείς σε υποβολές. Αυτή η ενότητα περιγράφει τους διάφορους τρόπους με τους οποίους μπορούμε να αναφερθούμε σε μία μόνο υποβολή.

Σύντομος αριθμός SHA-1

Το Git είναι αρκετά έξυπνο για να καταλάβει ποια υποβολή θέλαμε να πληκτρολογήσουμε αν δώσουμε τους πρώτους χαρακτήρες, αρκεί ο μερικός αριθμός SHA-1 να έχει μήκος τουλάχιστον τέσσερις χαρακτήρες και να είναι μονοσήμαντη —δηλαδή, μόνο ένα αντικείμενο στο τρέχον αποθετήριο αρχίζει με αυτό το μερικό SHA-1.

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

$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'

commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 14:58:32 2008 -0800

    added some blame and merge stuff

Σε αυτήν την περίπτωση, επιλέγουμε 1c002dd..... Αν τρέξουμε git show για να δούμε αυτήν την υποβολή, οι ακόλουθες εντολές είναι ισοδύναμες (με την προϋπόθεση ότι οι σύντομες εκδόσεις είναι μονοσήμαντες):

$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d

Το Git μπορεί να υπολογίσει μια σύντομη, μοναδική συντομογραφία για τις τιμές SHA-1. Εάν περάσουμε την επιλογή --abbrev-commit στην εντολή git log, στην έξοδο θα δούμε συντομότερες τιμές αλλά θα διατηρηθούν μοναδικές· εκ προεπιλογής χρησιμοποιούνται επτά χαρακτήρες αλλά χρησιμοποιούνται περισσότεροι αν είναι απαραίτητο για να παραμείνει ο αριθμός SHA-1 ασαφές:

$ git log --abbrev-commit --pretty=oneline
ca82a6d changed the version number
085bb3b removed unnecessary test code
a11bef0 first commit

Γενικά, οκτώ έως δέκα χαρακτήρες είναι περισσότεροι από αρκετοί για να είναι μοναδικοί σε ένα έργο.

Για παράδειγμα, στο έργο του πυρήνα Linux, το οποίο είναι ένα αρκετά μεγάλο έργο με πάνω από 450.000 υποβολές και 3,6 εκατομμύρια αντικείμενα, δεν υπάρχουν δύο αντικείμενα των οποίων τα SHA-1s να επικαλύπτονται σε περισσότερους από τους 11 πρώτους χαρακτήρες.

Note
Σχετικά με τον SHA-1

Πολλοί ανησυχούν κάποια στιγμή από κάποια σύμπτωση, θα υπάρχουν δύο αντικείμενα στο αποθετήριό τους, στα οποία αντιστοιχίζεται η ίδια τιμή SHA-1. Τι γίνεται τότε;

Εάν συμβεί να υποβάλουμε ένα αντικείμενο που έχει την ίδια τιμή SHA-1 με ένα προηγούμενο αντικείμενο στοαποθετήριό μας, το Git θα δει το προηγούμενο αντικείμενο που υπάρχει ήδη στη βάση δεδομένων μας του Git και θα υποθέσει ότι έχει ήδη γραφτεί. Εάν προσπαθήσουμε να ενημερώσουμε (checkout) κάποια στιγμή ξανά αυτό το αντικείμενο, θα παίρνουμε πάντα τα δεδομένα του πρώτου αντικειμένου.

Ωστόσο, θα πρέπει να γνωρίζουμε πόσο γελοία απίθανο είναι αυτό το σενάριο. Το εύρος του αριθμού SHA-1 είναι 20 bytes ή 160 bit. Ο αριθμός των τυχαία χαστούμενων αντικειμένων που απαιτούνται για να εξασφαλιστεί πιθανότητα 50% μιας και μόνο σύγκρουσης είναι περίπου 2<sup>80<\sup> (ο τύπος για τον προσδιορισμό της πιθανότητας σύγκρουσης είναι p=(n(n-1)/2)*(1/2^160)). 2<sup>80<\sup> είναι 1,2⋅10<sup>24<\sup> ή 1 εκατομμύριο δισεκατομμύρια δισεκατομμύρια. Αυτό είναι 1200 φορές μεγαλύτερο από το πλήθος των κόκκων άμμου στη Γη.

Ακολουθεί ένα παράδειγμα για να μας δώσει μια ιδέα του τι θα χρειαστεί για μια σύγκρουση SHA-1. Εάν και τα 6,5 δισεκατομμύρια ανθρώπων στη Γη προγραμμάτιζαν και κάθε δευτερόλεπτο, καθένας παρήγε κώδικα που ισοδυναμεί με ολόκληρο το ιστορικό του πυρήνα του Linux (3,6 εκατομμύρια αντικείμενα Git) και τα ωθούσε σε ένα τεράστιο αποθετήριο Git, θα απαιτούνταν περίπου 2 χρόνια μέχρις ότου αυτό το αποθετήριο περιείχε αρκετά αντικείμενα ώστε να υπάρχει πιθανότητα 50% μιας σύγκρουσης αντικειμένων SHA-1. Η πιθανότητα κάθε μέλος μίας ομάδας προγραμματιστών να δεχτεί επίθεση από λύκους σε άσχετα μεταξύ τους περιστατικά το ίδιο βράδυ είναι μεγαλύτερη από αυτήν.

Αναφορές κλάδων

Ο πιο απλός τρόπος για να καθορίσουμε μια υποβολή είναι όταν υπάρχει μια αναφορά κλάδου που δείχνει σε αυτήν. Σε αυτήν την περίπτωση μπορούμε να χρησιμοποιήσουμε ένα όνομα κλάδου σε οποιαδήποτε εντολή Git αναμένει ένα αντικείμενο υποβολής ή μια τιμή SHA-1. Για παράδειγμα, εάν θέλουμε να εμφανίσουμε το τελευταίο αντικείμενο υποβολής σε έναν κλάδο, οι ακόλουθες εντολές είναι ισοδύναμες, με την προϋπόθεση ότι ο κλάδος topic1 δείχνει στην ca82a6d:

$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1

Αν θέλουμε να δούμε σε ποιον συγκεκριμένο αριθμό SHA-1 δείχνει ένας κλάδος ή αν θέλουμε να δούμε τι σημαίνουν πραγματικά αυτά τα παραδείγματα από την οπτική γωνία των αριθμών SHA-1s, μπορούμε να χρησιμοποιήσουμε ένα εργαλείο διοχέτευσης Git με όνομα rev-parse. Μπορούμε να δούμε την ενότητα Εσωτερική λειτουργία του Git για περισσότερες πληροφορίες σχετικά με τα εργαλεία διοχέτευσης. Βασικά, η rev-parse υπάρχει για λειτουργίες χαμηλότερου επιπέδου και δεν έχει σχεδιαστεί για να χρησιμοποιείται σε καθημερινές λειτουργίες. Ωστόσο, μπορεί να μας βοηθήσει μερικές φορές όταν χρειάζεται να δούμε τι συμβαίνει πραγματικά. Εδώ μπορούμε να εκτελέσουμε rev-parse στον κλάδο μας.

$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949

RefLog Shortnames

Ένα από τα πράγματα που κάνει το Git στο παρασκήνιο ενώ εργαζόμαστε είναι να κρατά ένα reflog —ένα μητρώο του πού βρίσκονταν οι αναφορές του HEAD και των κλάδων μας τους τελευταίους μήνες.

Μπορούμε να δούμε το reflog μας χρησιμοποιώντας την εντολή git reflog:

$ git reflog
734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated
d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive.
1c002dd HEAD@{2}: commit: added some blame and merge stuff
1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
95df984 HEAD@{4}: commit: # This is a combination of two commits.
1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD

Κάθε φορά που η κορυφή του κλάδου μας ενημερώνεται για οποιονδήποτε λόγο, το Git αποθηκεύει αυτήν την πληροφορία για μας σε αυτό το προσωρινό ιστορικό. Μπορούμε επίσης να ορίσουμε παλαιότερες υποβολές με αυτά τα δεδομένα. Εάν θέλουμε να δούμε την πέμπτη προγενέστερη τιμή του HEAD του αποθετηρίου μας, μπορούμε να χρησιμοποιήσουμε την αναφορά @{n} που βλέπουμε στην έξοδο της reflog:

$ git show HEAD@{5}

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

$ git show master@{yesterday}

Αυτό μας δείχνει πού ήταν η άκρη του κλάδου χθες. Αυτή η τεχνική λειτουργεί μόνο για δεδομένα που εξακολουθούν να βρίσκονται στο reflog μας, συνεπώς δεν μπορούμε να τα χρησιμοποιήσουμε για να αναζητήσουμε υποβολές παλαιότερες από μερικούς μήνες.

Για να δούμε πληροφορίες reflog μορφοποιημένες όπως η έξοδος της git log, μπορούμε να εκτελέσουμε git log -g:

$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'

Είναι σημαντικό να σημειώσουμε ότι οι πληροφορίες reflog είναι αυστηρά τοπικές —είναι ένα μητρώο του τι έχουμε κάνει στο δικό μας αποθετήριο. Οι αναφορές δεν θα είναι οι ίδιες στο αντίγραφο του αποθετηρίου κάποιου άλλου· και αμέσως μετά την αρχική κλωνοποίηση ενός αποθετηρίου, έχουμε ένα κενό reflog, αφού δεν έχει πραγματοποιηθεί καμία δραστηριότητα στο αποθετήριό μας. Αν εκτελέσουμε git show HEAD@{2.months.ago} θα λειτουργήσει μόνο εφόσον έχουμε κλωνοποιήσει το έργο πριν από τουλάχιστον δύο μήνες —αν το κλωνοποιήσαμε πριν από πέντε λεπτά, δεν θα πάρουμε αποτελέσματα.

Ancestry References

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

$ git log --pretty=format:'%h %s' --graph
* 734713b fixed refs handling, added gc auto, updated tests
*   d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd added some blame and merge stuff
|/
* 1c36188 ignore *.gem
* 9b29157 add open3_detach to gemspec file list

Στη συνέχεια, μπορούμε να δούμε την προηγούμενη υποβολή γράφοντας HEAD ^, που σημαίνει “τον γονέα της HEAD”:

$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'

Μπορούμε επίσης να ορίσουμε έναν αριθμό μετά το ^ —για παράδειγμα, d921970^2 σημαίνει “το δεύτερο γονέα της d921970”. Αυτή η σύνταξη είναι χρήσιμη μόνο για υποβολές συγχώνευσης, οι οποίες έχουν περισσότερους από έναν γονείς. Ο πρώτος γονέας είναι ο κλάδος στο οποίο βρισκόμασταν όταν συγχωνεύσαμε και ο δεύτερος είναι η υποβολή του κλάδου τον οποίο συγχωνεύσαμε:

$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 14:58:32 2008 -0800

    added some blame and merge stuff

$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date:   Wed Dec 10 22:22:03 2008 +0000

    Some rdoc changes

Η άλλη κύρια γενεαλογική σήμανση είναι η ~. Αυτό αναφέρεται επίσης στον πρώτο γονέα, έτσι ώστε οι HEAD~ και HEAD^ είναι ισοδύναμες. Η διαφορά γίνεται αντιληπτή όταν προσαρτήσουμε έναν αριθμό. HEAD~2 σημαίνει “ο πρώτος γονέας του πρώτου γονέα” ή “ο παππούς” —διαπερνά τους πρώτους γονείς τόσες φορές όσες καθορίζουμε. Για παράδειγμα στο ιστορικό που αναφέρθηκε νωρίτερα, η HEAD~3 θα ήταν:

$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date:   Fri Nov 7 13:47:59 2008 -0500

    ignore *.gem

Αυτό μπορεί επίσης να γραφεί επίσης και HEAD^^^, το οποίο και πάλι είναι ο πρώτος γονέας του πρώτου γονέα του πρώτου γονέα:

$ git show HEAD^^^
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date:   Fri Nov 7 13:47:59 2008 -0500

    ignore *.gem

Μπορούμε επίσης να συνδυάσουμε αυτές τις σύνταξεις —μπορούμε να πάρουμε τον δεύτερο γονέα της προηγούμενης αναφοράς (με την προϋπόθεση ότι ήταν μια υποβολή συγχώνευσης) χρησιμοποιώντας την HEAD~3^2 και ούτω καθεξής.

Εύρος υποβολών

Τώρα που μπορούμε να καθορίσουμε μεμονωμένες υποβολές, ας δούμε πώς θαα καθορίσουμε ένα εύρος υποβολών. Αυτό είναι ιδιαίτερα χρήσιμο για τη διαχείριση των κλάδων μας —αν έχουμε πολλούς κλάδους, μπορούμε να χρησιμοποιήσουμε προσδιορισμούς εύρους για να απαντήσουμε σε ερωτήσεις όπως “Ποια δουλειά σε αυτόν τον κλάδο δεν έχω ακόμα συγχωνεύσει στον κύριο κλάδο μου;”

Διπλή τελεία

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

Παράδειγμα ιστορικού με επιλογή εύρους.
Figure 136. Παράδειγμα ιστορικού με επιλογή εύρους.

Θέλουμε να δούμε τι υπάρχει στον κλάδο experiment δεν έχει ακόμη συγχωνευθεί στον κλάδο master. Μπορούμε να ζητήσουμε από το Git να μας παρουσιάσει ένα μητρώο μόνο αυτών των υποβολών με την master..experiment —που σημαίνει ``όλες τις υποβολές που είναι προσπελάσιμες από τον experiment αλλά δεν είναι προσπελάσιμες από τον master. Για λόγους συντομίας και ευκρίνειας, σε αυτά τα παραδείγματα θα χρησιμοποιούντα τα γράμματα των αντικειμένων υποβολής της προηγούμενης εικόνας αντί του πραγματικού μητρώου που θα παίρναμε με τη σειρά που θα εμφανίζονταν:

$ git log master..experiment
D
C

Εάν, από την άλλη, θέλουμε να δούμε το αντίθετο —όλες τις υποβολές στον master που δεν βρίσκονται στον experiment — μπορούμε να αντιστρέψουμε τα ονόματα κλάδων. Η experiment..master μας δείχνει ό,τι υπάρχει στον master που δεν είναι προσπελάσιμο από τον experiment:

$ git log experiment..master
F
E

Αυτό είναι χρήσιμο όταν θέλουμε να ενημερώσουμε τον κλάδο experiment και να δούμε μία προεπισκόπηση του τι πρόκειται να συγχωνεύσουμε. Μια άλλη πολύ συχνή χρήση αυτής της σύνταξης είναι να δούμε τι πρόκειται να ωθήσουμε σε ένα απομακρυσμένο αποθετήριο:

$ git log origin/master..HEAD

Αυτή η εντολή μάς δείχνει όλες τις υποβολές του τρέχοντος κλάδου μας που δεν ανήκουν στον κλάδο master του απομακρυσμένου origin. Εάν εκτελέσουμε ένα git push και ο τρέχοντας κλάδος μας παρακολουθεί τον origin/master, οι υποβολές που αναφέρονται από την git log orig/master..HEAD είναι οι υποβολές που θα μεταφερθούν στον διακομιστή. Μπορούμε επίσης να παραλείψουμε τη μιας πλευράς της σύνταξης, οπότε το Git να αντιλαμβάνεται HEAD. Για παράδειγμα, μπορούμε να πάρουμε τα ίδια αποτελέσματα με το προηγούμενο παράδειγμα πληκτρολογώντας την git log orig/master.. —ο Git διαβάζει HEAD αν λείπει μία πλευρά.

Πολλαπλά σημεία

Η σύνταξη διπλής τελείας είναι χρήσιμη ως συντομογραφία· αλλά ίσως θέλουμε να καθορίσουμε περισσότερους από δύο κλάδους για να υποδείξουμε τις εκδόσεις που θέλουμε, όπως για παράδειγμα να δούμε τι υποβολές υπάρχουν σε οποιονδήποτε από τους διάφορους κλάδους που δεν ανήκουν στον κλάδο στον οποίο βρισκόμαστε. Το Git μάς επιτρέπει να το κάνουμε αυτό χρησιμοποιώντας είτε τον χαρακτήρα ^ είτε --not πριν από οποιαδήποτε αναφορά από την οποία δεν θέλουμε να δούμε προσπελάσιμες υποβολές. Έτσι, αυτές οι τρεις εντολές είναι ισοδύναμες:

$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA

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

$ git log refA refB ^refC
$ git log refA refB --not refC

Αυτό είναι ένα πολύ ισχυρό σύστημα ερωτημάτων εκδόσεων που μας βοηθά να καταλαβαίνουμε τι υπάρχει στους κλάδους μας.

Τριπλή τελεία

Η τελευταία σημαντική επιλογής εύρους είναι η σύνταξη με την τριπλή τελεία, η οποία καθορίζει όλες τις υποβολές που είναι προσπελάσιμες από μία από τις δύο αναφορές, αλλά όχι και από τις δύο. Ας ανατρέξουμε στο παράδειγμα του ιστορικού υποβολής στην ενότητα Παράδειγμα ιστορικού με επιλογή εύρους.. Αν θέλουμε να δούμε τι υπάρχει στον master ή τον experiment αλλά όχι και στους δύο, μπορούμε να εκτελέσουμε

$ git log master...experiment
F
E
D
C

Επαναλαμβάνουμε ότι αυτό μας δίνει την έξοδο μίας απλής log, αλλά μας δείχνει μόνο τις πληροφορίες υποβολής για αυτές τις τέσσερις υποβολές και εμφανίζονται στην παραδοσιακή διάταξη κατά την ημερομηνία υποβολής.

Ένας κοινός διακόπτης που θα χρησιμοποιείται με την εντολή log σε αυτήν την περίπτωση είναι ο --left-right, που μας δείχνει σε ποια πλευρά του εύρους βρίσκεται η κάθε υποβολή. Αυτό καθιστά τα δεδομένα πιο χρήσιμα:

$ git log --left-right master...experiment
< F
< E
> D
> C

Με αυτά τα εργαλεία, μπορούμε πολύ πιο εύκολα να δώσουμε στο Git να γνωρίζει ποια υποβολή ή ποιες υποβολές θέλουμε να επιθεωρήσουμε.