Git
Chapters ▾ 2nd Edition

7.10 Εργαλεία του Git - Αποσφαλμάτωση με το Git

Αποσφαλμάτωση με το Git

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

Επισημείωση αρχείων

Αν εντοπίζουμε ένα σφάλμα στον κώδικά μας και θέλουμε να μάθουμε πότε εισήχθη και γιατί, η επισημείωση (annotation) του αρχείου είναι συχνά το καλύτερο εργαλείο μας. Μας δείχνει ποια ήταν η τελευταία υποβολή που τροποποποίησε κάθε γραμμή οποιουδήποτε αρχείου. Έτσι, εάν δούμε ότι μια μέθοδος στον κώδικά μας έχει σφάλματα, μπορούμε να επισημειώσουμε το αρχείο με git blame για να δούμε πότε άλλαξε τελευταία φορά κάθε γραμμή της μεθόδου και από ποιον. Αυτό το παράδειγμα χρησιμοποιεί την επιλογή -L για να περιορίσει την έξοδο στις γραμμές 12 έως 22:

$ git blame -L 12,22 simplegit.rb
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 12)  def show(tree = 'master')
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 13)   command("git show #{tree}")
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 14)  end
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 15)
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 16)  def log(tree = 'master')
79eaf55d (Scott Chacon  2008-04-06 10:15:08 -0700 17)   command("git log #{tree}")
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 18)  end
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 19)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20)  def blame(path)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21)   command("git blame #{path}")
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22)  end

Παρατηρούμε ότι το πρώτο πεδίο είναι ο μερικός αριθμός SHA-1 της υποβολής που τροποποίησε τελευταία αυτήν τη γραμμή. Τα επόμενα δύο πεδία είναι τιμές που εξάχθηκαν από αυτήν την υποβολή —το όνομα συγγραφέα και η ημερομηνία επεξεργασίας αυτής της υποβολής— για να μπορούμε εύκολα να δούμε ποιος τροποποίησε αυτήν τη γραμμή και πότε. Ακολουθούν ο αριθμός γραμμής και το περιεχόμενο του αρχείου. Ο αριθμός SHA-1 ^4832fe2 υποδηλώνει ότι οι γραμμές με αυτόν τον αριθμό ήταν στην αρχική υποβολή αυτού του αρχείου. Αυτή είναι η υποβολή με την οποία το αρχείο αυτό προστέθηκε για πρώτη φορά σε αυτό το έργο και οι γραμμές αυτές δεν έχουν αλλάξει από τότε. Αυτό δημιουργεί μία μικρή σύγχυση, καθώς μέχρι στιγμής έχουμε δει τουλάχιστον τρεις διαφορετικούς τρόπους με τους οποίους το Git χρησιμοποιεί το ^ για να τροποποιήσει τον αριθμό SHA-1 μίας υποβολής, αλλά εδώ σημαίνει αυτό το πράγμα.

Ένα άλλο πολύ ωραίο χαρακτηριστικό για το Git είναι ότι δεν παρακολουθεί απόλυτα το όνομα του αρχείου. Καταγράφει τα στιγμιότυπα και στη συνέχεια προσπαθεί να καταλάβει τι μετονομάστηκε σιωπηρά, μετά την μετονομασία. Μία από τις πιο ενδιαφέρουσες λειτουργίες αυτού είναι ότι μπορούμε να του ζητήσουμε επίσης να καταλάβει όλα τα είδη των κινήσεων του κώδικα. Εάν μεταβιβάσουμε την επιλογή -C στην git blame, το Git αναλύει το αρχείο που επισημειώνουμε και προσπαθεί να καταλάβει από πού προήλθαν αποσπάσματα κώδικα μέσα του, εφόσον είχαν αντιγραφεί από κάπου αλλού. Για παράδειγμα, ας πούμε ότι επανασχεδιάζουμε ένα αρχείο που ονομάζεται GITServerHandler.m σε πολλά αρχεία, ένα από τα οποία είναι GITPackUpload.m. Αν κατηγορήσουμε το GITPackUpload.m με την επιλογή -C, μπορούμε να δούμε από πού προήλθαν τμήματα του κώδικα:

$ git blame -C -L 141,153 GITPackUpload.m
f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 145)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 146)         NSString *parentSha;
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 147)         GITCommit *commit = [g
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 149)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 150)
56ef2caf GITServerHandler.m (Scott 2009-01-05 151)         if(commit) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152)                 [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)

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

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

Ας υποθέσουμε ότι μόλις ωθήσαμε μια έκδοση του κώδικά μας στην παραγωγή, παίρνουμε αναφορές σφαλμάτων σχετικά με κάτι που δεν συνέβαινε στο περιβάλλον ανάπτυξης και δεν μπορούμε να φανταστούμε γιατί ο κώδικας το κάνει αυτό. Πηγαίνουμε πίσω στον κώδικά μας και αποδεικνύεται ότι μπορούμε να αναπαραγάγουμε το πρόβλημα, αλλά δεν μπορούμε να καταλάβουμε τι το δημιουργεί. Μπορούμε να διχοτομήσουμε τον κώδικα για να το ανακαλύψουμε. Αρχικά τρέχουμε την git bisect start για να ξεκινήσει η διαδικασία της διοχοτόμησης και στη συνέχεια την git bisect bad για να πούμε στο σύστημα ότι η τρέχουσα υποβολή που χρησιμοποιούμε είναι χαλασμένη. Στη συνέχεια, πρέπει να πούμε στην bisect ποια ήταν η τελευταία γνωστή καλή κατάσταση, χρησιμοποιώντας την git bisect good [good_commit]:

$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo

Το Git κατάλαβε ότι περίπου 12 υποβολές ήρθαν μεταξύ της υποβολής που σημειώσαμε ως της τελευταίας καλής υποβολής (v1.0) και της τρέχουσας κακής έκδοσης, και μετέβη (checkout) τη μεσαία για εμάς. Σε αυτό το σημείο, μπορούμε να εκτελέσουμε κάποια δοκιμασία για να διαπιστώσουμε εάν υπάρχει το πρόβλημα σε αυτήν την υποβολή. Εάν υπάρχει, τότε εισήχθη κάποια στιγμή πριν από αυτήν τη μεσαία υποβολή· αν δεν υπάρχει, τότε το πρόβλημα εισήχθη κάποια στιγμή μετά τη μεσαία υποβολή. Αποδεικνύεται ότι το πρόβλημα δεν υπάρχει εδώ και το λέμε αυτό στο Git ότι πληκτρολογώντας git bisect good και συνεχίζουμε την αναζήτηση του σφάλματος:

$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing

Τώρα είμαστε σε μια άλλη υποβολή, στα μισά του δρόμου μεταξύ εκείνης που μόλις δοκιμάσαμε και της κακής υποβολής. Πραγματοποιούμε ξανά τη δοκιμή μας και διαπιστώνουμε ότι αυτή η υποβολή είναι χαλασμένη, οπότε ενημερώνουμε σχετικά το Git με την git bisect bad:

$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table

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

$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <pjhyett@example.com>
Date:   Tue Jan 27 14:48:32 2009 -0800

    secure this thing

:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M  config

Εφόσον έχουμε τελειώσει, θα πρέπει να εκτελέσουμε την git bisect reset για να επαναφέρουμε τον HEAD στο σημείο που ήμασταν πριν ξεκινήσουμε αλλιώς θα καταλήξουμε σε μια περίεργη κατάσταση:

$ git bisect reset

Αυτό είναι ένα ισχυρό εργαλείο που μπορεί να μας βοηθήσει να ελέγξουμε εκατοντάδες υποβολές για το πού εισήχθη ένα σφάλμα μέσα σε λίγα λεπτά. Μάλιστα, αν έχουμε ένα script που θα τερματίσει με έξοδο 0 εάν το έργο είναι καλό ή όχι-0, αν το έργο είναι κακό, μπορούμε να αυτοματοποιήσουμε πλήρως την git bisect. Καταρχάς της λέμε ξανά το εύρος της διχοτόμησης παρέχοντας τις γνωστές κακές και καλές υποβολές. Αυτό μπορούμε να το κάνουμε με την εντολή bisect start, αναφέροντας πρώτα τη γνωστή κακή υποβολή και μετά τη γνωστή καλή υποβολη:

$ git bisect start HEAD v1.0
$ git bisect run test-error.sh

Με αυτόν τον τρόπο εκτελείται αυτόματα το test-error.sh σε κάθε ελεγχόμενη απόσπαση μέχρι να βρει η Git την πρώτη χαλασμένη υποβολή. Μπορούμε επίσης να εκτελέσουμε κάτι σαν make ή make tests ή ό,τι διαθέτουμε που εκτελεί αυτοματοποιημένες δοκιμές για εμάς.