Chapters ▾ 2nd Edition

A2.2 Appendix B: Ενσωμάτωση του Git στις εφαρμογές μας - Libgit2

Libgit2

© Μια άλλη επιλογή στη διάθεσή μας είναι να χρησιμοποιήσουμε το Libgit2. Το Libgit2 είναι μια υλοποίηση του Git χωρίς εξαρτήσεις (dependencies), με έμφαση σε ένα ωραίου API για χρήση σε άλλα προγράμματα. Μπορούμε να το βρούμε στην https://libgit2.org.

Αρχικά, ας ρίξουμε μια ματιά πως μοιάζει το API για C. Ακολουθεί ένα παράδειγμα τυφώνας!

// Open a repository
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");

// Dereference HEAD to a commit
git_object *head_commit;
error = git_revparse_single(&head_commit, repo, "HEAD^{commit}");
git_commit *commit = (git_commit*)head_commit;

// Print some of the commit's properties
printf("%s", git_commit_message(commit));
const git_signature *author = git_commit_author(commit);
printf("%s <%s>\n", author->name, author->email);
const git_oid *tree_id = git_commit_tree_id(commit);

// Cleanup
git_commit_free(commit);
git_repository_free(repo);

Οι πρώτες δύο γραμμές ανοίγουν ένα αποθετήριο Git. Ο τύπος git_repository αντιπροσωπεύει έναν χειριστή ελέγχου (handle) σε ένα αποθετήριο με κρυφή μνήμη (cache) στη μνήμη. Αυτή είναι η απλούστερη μέθοδος εφόσον γνωρίζουμε την ακριβή διαδρομή στον κατάλογο εργασίας του αποθετηρίου ή στον φάκελο .git. Υπάρχούν επίσης η git_repository_open_ext που περιλαμβάνει επιλογές αναζήτησης, η git_clone και οι σχετικές εντολές για τη δημιουργία τοπικού κλώνου ενός απομακρυσμένου αποθετηρίου και η git_repository_init για τη δημιουργία ενός εντελώς νέου αποθετηρίου.

Το δεύτερο κομμάτι του κώδικα χρησιμοποιεί σύνταξη rev-parse (βλ. ενότητα Αναφορές κλάδων σχετικά) για να πάρει την υποβολή στην οποία δείχνει τελικά ο HEAD. Ο τύπος που επιστρέφεται είναι ένας δείκτης git_object, που αντιπροσωπεύει κάτι που υπάρχει στη βάση δεδομένων αντικειμένων Git για ένα αποθετήριο. Το git_object είναι στην πραγματικότητα ένας τύπος “parent” για πολλά διαφορετικά είδη αντικειμένων· η διάταξη μνήμης για καθέναν από τους τύπους “child” είναι ίδια με εκείνη του git_object, ώστε να μπορούμε να κάνουμε μετατροπή στο σωστό με ασφάλεια. Σε αυτήν την περίπτωση, η git_object_type(commit) θα επέστρεφε GIT_OBJ_COMMIT, επομένως είναι ασφαλές να κάνουμε μετατροπή σε έναν δείκτη git_commit.

Το επόμενο κομμάτι δείχνει τον τρόπο πρόσβασης στις ιδιότητες της υποβολής. Η τελευταία γραμμή εδώ χρησιμοποιεί έναν τύπο git_oid· αυτή είναι η αναπαράσταση του Libgit2 για έναν αριθμό SHA-1.

Από αυτό το παράδειγμα, έχουν αρχίσει να αναδύονται δύο μοντέλα:

  • Αν δηλώσουμε έναν δείκτη και περάσουμε μια αναφορά σε αυτό σε μια κλήση Libgit2, αυτή η κλήση θα επιστρέψει πιθανώς έναν ακέραιο κωδικό σφάλματος.   Η τιμή 0 υποδηλώνει επιτυχία· ο,τιδήποτε άλλο υποδηλώνει σφάλμα.

  • Αν το Libgit2 συγκεντρώσει έναν δείκτη για εμάς, είμαστε υπεύθυνοι για την απελευθέρωσή του.

  • Αν το Libgit2 επιστρέψει έναν δείκτη const από μια κλήση, δεν χρειάζεται να τον απελευθερώσουμε, αλλά θα ακυρωθεί όταν το αντικείμενο στο οποίο ανήκει ελευθερωθεί.

  • Ο προγραμματισμός σε C είναι λίγο οδυνηρός.

Αυτό το τελευταίο σημαίνει ότι δεν είναι πολύ πιθανό ότι θα γράφουμε C όταν χρησιμοποιούμε το Libgit2. Ευτυχώς, υπάρχει ένας αριθμός γλωσσών script που είναι διαθέσιμες και καθιστούν αρκετά εύκολο να δουλέψουμε με αποθετήρια Git από τη συγκεκριμένη γλώσσα και το περιβάλλον μας. Ας ρίξουμε μια ματιά στο παραπάνω παράδειγμα που γράφτηκε χρησιμοποιώντας τις συνδέσεις (bindings) Ruby για το Libgit2, οι οποίες ονομάζονται Rugged και μπορούν να βρεθούν στην https://github.com/libgit2/rugged.

repo = Rugged::Repository.new('path/to/repository')
commit = repo.head.target
puts commit.message
puts "#{commit.author[:name]} <#{commit.author[:email]}>"
tree = commit.tree

Όπως βλέπουμε ο κώδικας είναι πιο συμμαζεμένος. Καταρχάς, η Rugged χρησιμοποιεί εξαιρέσεις (exceptions)· μπορεί να επικαλεστεί σφάλματα όπως ConfigError ή ObjectError για να σηματοδοτήσει συνθήκες σφαλμάτων. Κατά δεύτερο λόγο, δεν υπάρχει ρητή απελευθέρωση πόρων, διότι η Ruby έχει αυτόματο συλλέκτη σκουπιδιών. Ας δούμε ένα λίγο πιο σύνθετο παράδειγμα: υλοποίηση μίας υποβολής εκ του μηδενός.