Chapters ▾ 2nd Edition

5.2 Κατανεμημένο Git - Συνεισφέροντας σε ένα έργο

Συνεισφέροντας σε ένα έργο

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

Ο πρώτος παράγοντας είναι ο ενεργός αριθμός συνεργατών --πόσοι χρήστες συμβάλλουν ενεργά στον κώδικα αυτού του έργου και πόσο συχνά; Σε πολλές περιπτώσεις, θα έχουμε δύο ή τρεις προγραμματιστές με κάποιες υποβολές ανά ημέρα ή και λιγότερες για κάπως αδρανή έργα. Για μεγαλύτερες εταιρείες ή έργα, ο αριθμός των προγραμματιστών μπορεί να φτάσει τις χιλιάδες, με εκατοντάδες ή χιλιάδες υποβολές να καταφτάνουν κάθε μέρα. Αυτός ο παράγοντας είναι σημαντικός επειδή όσο περισσότεροι προγραμματιστές εμπλέκονται, τόσο περισσότερα θέματα έχουμε όσον αφορά στην καθαρή εφαρμογή και ευκολία συγχώνευσης του κώδικά μας. Οι αλλαγές που υποβάλουμε ενδέχεται να καταστούν παρωχημένες ή να αλλοιωθούν σε σημαντικό βαθμό από εργασίες που συγχωνεύονται ενώ εργαζόμαστε ή ενώ οι αλλαγές μας αναμένουν έγκριση ή εφαρμογή. Πώς, λοιπόν, μπορούμε να διατηρήσουμε τον κώδικά μας συνεχώς ενημερωμένο και τις υποβολές μας έγκυρες;

Ο επόμενος παράγοντας είναι η ροή εργασίας που χρησιμοποιείται στο έργο. Είναι συγκεντρωτική με κάθε προγραμματιστή να έχει ισότιμη πρόσβαση για εγγραφή στην κύρια γραμμή παραγωγής κώδικα; Έχει το πρόγραμμα κάποιον συντηρητή ή διαχειριστή ενσωμάτωσης, ο οποίος ελέγχει όλα τα επιθέματα; Περνούν τα επιθέματα κάποια διαδικασία ελέγχου από συνεργάτες ή έγκρισης; Συμμετέχουμε εμείς σε αυτή τη διαδικασία; Υπάρχει σύστημα υπαρχηγού και πρέπει να υποβάλλουμε την εργασία μας πρώτα σε αυτόν;

Ο επόμενος παράγοντας είναι η το επίπεδο πρόσβασής μας όσον αφορά στις υποβολές. Η ροή εργασίας που απαιτείται για να συνεισφέρουμε σε ένα έργο είναι πολύ διαφορετική εάν έχουμε πρόσβαση για εγγραφή στο έργο απ' ό,τι αν δεν έχουμε. Εάν δεν έχουμε πρόσβαση για εγγραφή, πως προτιμά το έργο να δέχεται συνεισφερόμενη εργασία; Υπάρχει κάποια σχετική πολιτική; Πόση δουλειά υποβάλουμε κάθε φορά; Πόσο συχνά συμβάλλουμε;

Όλα αυτά τα ερωτήματα επηρεάζουν τον τρόπο με τον οποίο συνεισφέρουμε αποτελεσματικά σε ένα έργο και ποιες ροές εργασίας προτιμώνται ή μας είναι διαθέσιμες. Θα καλύψουμε πτυχές καθεμιάς από αυτές τις ροές εργασίας σε μια σειρά περιπτώσεων χρήσης, από απλές μέχρι περίπλοκες· θα πρέπει να είμαστε σε θέση να κατασκευάζουμε τις ροές εργασίας που χρειαζόμαστε κάθε φορά από αυτά τα παραδείγματα.

Κατευθυντήριες γραμμές για τις υποβολές

Πριν ξεκινήσουμε να εξετάζουμε τις συγκεκριμένες περιπτώσεις χρήσης, ας δούμε λίγο για τα μηνύματα υποβολής. Η ύπαρξη καλών κατευθυντήριων γραμμών σχετικά με τη δημιουργία υποβολών και η τήρησή τους διευκολύνουν την εργασία με το Git και τη συνεργασία με άλλους. Το έργο Git παρέχει ένα έγγραφο που παραθέτει πολλές καλές συμβουλές για τη δημιουργία υποβολών για επιθέματα — μπορούμε να το διαβάσουμε στον πηγαίο κώδικα του Git στο αρχείο Documentation/SubmittingPatches.

Καταρχάς δεν θέλουμε οι υποβολές μας να έχουν προβλήματα με τους λευκούς χαρακτήρες (whitespace). Το Git παρέχει έναν εύκολο τρόπο για να ελέγξουμε τέτοιου είδους σφάλματα — προτού υποβάλλουμε, εκτελούμε την εντολή git diff --check, η οποία προσδιορίζει πιθανά σφάλματα διαστημάτων και μας τα παραθέτει.

Έξοδος μίας `git diff --check`
Figure 56. Έξοδος μίας git diff --check

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

Δεύτερον, προσπαθούμε ώστε κάθε υποβολή να έχει ένα λογικά διακριτό σύνολο αλλαγών. Αν είναι δυνατό, προσπαθούμε να κάνουμε τις αλλαγές μας εύπεπτες — ας μην γράφουμε κώδικα για ένα ολόκληρο Σαββατοκύριακο σε πέντε διαφορετικά θέματα και στη συνέχεια τα υποβάλλουμε όλα ως μια τεράστια υποβολή τη Δευτέρα. Ακόμη και αν δεν υποβάλλουμε μέσα στο Σαββατοκύριακο, χρησιμοποιούμε το στάδιο καταχώρησης τη Δευτέρα για να διαχωρίσουμε την εργασία μας σε τουλάχιστον μία υποβολή ανά ζήτημα, με ένα χρήσιμο μήνυμα ανά υποβολή. Εάν ορισμένες από τις αλλαγές τροποποιούν το ίδιο αρχείο, προσπαθούμε να χρησιμοποιήσουμε το git add --patch ώστε να τοποθετήσουμε μερικώς αρχεία στο στάδιο καταχώρησης (αυτό καλύπτεται σε βάθος στην ενότητα Διαδραστική εργασία με το στάδιο καταχώρισης). Το στιγμιότυπο του έργου στην άκρη του κλάδου είναι το ίδιο είτε πραγματοποιούμε μία υποβολή είτε πέντε, εφόσον όλες οι αλλαγές προστίθενται σε κάποιο σημείο, οπότε προσπαθούμε να διευκολύνουμε τους συνεργάτες μας όταν θα πρέπει να ελέγξουν τις αλλαγές μας.

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

Το τελευταίο πράγμα που πρέπει να έχουμε υπόψη είναι το μήνυμα υποβολής. Η δημιουργία μηνυμάτων υψηλής ποιότητας διευκολύνει τη χρήση του Git και τη συνεργασία. Έχουμε ως γενικό κανόνα τα μηνύματά μας να ξεκινούν με μία μόνο γραμμή που δεν υπερβαίνει τους 50 χαρακτήρες και περιγράφει συνοπτικά το σύνολο αλλαγών, να ακολουθεί μια κενή γραμμή και στη συνέχεια να ακολουθεί μια πιο λεπτομερής εξήγηση. Το έργο Git απαιτεί η λεπτομερέστερη εξήγηση να περιλαμβάνει το κίνητρό μας για την αλλαγή και να αντιπαραβάλλουμε την εφαρμογή της με την προηγούμενη συμπεριφορά — αυτή είναι μια καλή κατευθυντήρια γραμμή που πρέπει να ακολουθούμε. Επίσης είναι καλή ιδέα να χρησιμοποιούμε την προστακτική έγκλιση σε αυτά τα μηνύματα: “Διόρθωσε bug” και όχι “Διόρθωσα bug”, ή “Διορθώνει bug”. Ακολουθεί ένα πρότυπο που μπορούμε να ακολουθούμε και το οποίο έχουμε τροποποιήσει ελαφρά από αυτό που originally written by Tim Pope:

Σύντομη (το πολύ 50 χαρακτήρες) περίληψη των αλλαγών

Λεπτομερέστερη περιγραφή, εφόσον είναι απαραίτητη.  Αναδιπλώνετε σε περίπου 72
χαρακτήρες. Σε κάποιες περιστάσεις η πρώτη γραμμή αντιμετωπίζεται ως το θέμα
ενός e-mail και οι υπόλοιπες ως το σώμα του. Η κενή γραμμή που
χωρίζει την περίληψη από τη λεπτομερή περιγραφή είναι σημαντική (εκτός κι αν η
λεπτομερής περιγραφή παραλείπεται τελείως)· εργαλεία όπως η αλλαγή βάσης (rebase)
μπορεί να προκαλέσουν σύγχυση αν εκτελούμε και δύο ταυτόχρονα.

Γράφουμε το μήνυμα υποβολής μας στην προστακτική : "Διόρθωσε bug" και
όχι "Διόρθωσα bug" ή "Διορθώνει bug."  Αυτή η σύμβαση ταιριάζει
με τα μηνύματα υποβολής που δημιουργούν εντολές όπως οι git merge και git revert.

Περαιτέρω παράγραφοι έρχονται μετά από κενές γραμμές.

- Λίστες με κουκκίδες είναι αποδεκτές

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

- Χρησιμοποιούμε προεξοχή πρώτης γραμμής

Αν όλα τα μηνύματα υποβολών μας έχουν αυτή τη μορφή, θα διευκολυνθούμε τόσο εσείς όσο και οι συνεργάτες μας. Tο έργο Git έχει καλά μορφοποιημένα μηνύματα υποβολών. Αν τρέξετε git log --no-merges θα δείτε πως φαίνεται ένα όμορφα μορφοποιημένο ιστορικό υποβολών ενός έργου.

Note
Κάντε ό,τι λέμε, όχι ό,τι κάνουμε.

Για χάρη συντομίας, πολλά από τα παραδείγματα αυτού του βιβλίου δεν έχουν μηνύματα υποβολών που είναι μορφοποιημένα όμορφα, όπως παραπάνω· αντί γι' αυτό χρησιμοποιήστε την επιλογή -m στην εντολή git commit.

Πιο απλά, κάντε ό,τι λέμε, όχι ό,τι κάνουμε.

Ιδιωτικές μικρές ομάδες

Η απλούστερη ρύθμιση που ενδεχομένως θα συναντήσουμε είναι ένα ιδιωτικό έργο με έναν ή δύο ακόμα προγραμματιστές. “Ιδιωτικό” εδώ σημαίνει κλειστού-κώδικα — που δεν είναι προσβάσιμος από τον έξω κόσμο. Εμείς και όλοι οι άλλοι προγραμματιστές έχουμε πρόσβαση ώθησης στο αποθετήριο.

Σε αυτό το περιβάλλον, μπορούμε να ακολουθήσουμε μια ροή εργασίας παρόμοια με αυτή που θα ακολουθούσαμε αν χρησιμοποιούσαμε το Subversion ή κάποιο άλλο συγκεντρωτικό σύστημα. Συνεχίζουμε να έχουμε πλεονεκτήματα όπως η υποβολή εκτός σύνδεσης και η απλούστερη διακλάδωση και συγχώνευση, αλλά η ροή εργασίας μπορεί να είναι πολύ παρόμοια· η κύρια διαφορά είναι ότι οι συγχωνεύσεις συμβαίνουν στην πλευρά του πελάτη αντί στον διακομιστή κατά τη διάρκεια της υποβολής. Ας δούμε τι μπορεί να συμβεί όταν δύο προγραμματιστές αρχίζουν να συνεργάζονται σε ένα κοινό αποθετήριο. Ο πρώτος προγραμματιστής, ο Τζον, κλωνοποιεί το αποθετήριο, κάνει μια αλλαγή και την υποβάλλει τοπικά. Τα μηνύματα πρωτοκόλλου έχουν αντικατασταθεί με …​ σε αυτά τα παραδείγματα για να τα συντομεύσουμε κάπως.

# John's Machine
$ git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'Remove invalid default value'
[master 738ee87] Remove invalid default value
 1 files changed, 1 insertions(+), 1 deletions(-)

Η δεύτερη προγραμματίστρια, η Τζέσικα, κάνει το ίδιο πράγμα — κλωνοποιεί το αποθετήριο και κάνει μια αλλαγή:

# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'Add reset task'
[master fbff5bc] Add reset task
 1 files changed, 1 insertions(+), 0 deletions(-)

Τώρα, η Τζέσικα ωθεί την εργασία της στον διακομιστή:

# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
   1edee6b..fbff5bc  master -> master

Η τελευταία γραμμή της εξόδου δείχνει ένα χρήσιμο μήνυμα που επέστρεψε η ώθηση. Η βασική μορφή είναι <oldref>..<newref> fromref → toref, όπου oldref σημαίνει την παλιά αναφορά, newref σημαίνει την νέα αναφορά, fromref είναι το όνομα της τοπικής αναφοράς που ωθήθηκε και toref είναι το όνομα της απομακρυσμένης αναφοράς που ενημερώθηκε. Θα βλέπουμε παρόμοια με αυτό μηνύματα στις συζητήσεις, οπότε το να έχουμε μια ιδέα του τι σημαίνουν θα μας βοηθήσει να καταλαβαίνουμε τις διάφορες καταστάσεις των αποθετηρίων. Περισσότερες λεπτομέρειες υπάρχουν στην τεκμηρίωση της εντολής git-push.

Συνεχίζοντας το παράδειγμα, λίγο αργότερα, ο Τζον κάνει κάποιες αλλαγές, τις υποβάλει στο τοπικό του αποθετήριο και προσπαθεί να τις ωθήσει στον ίδιο διακομιστή:

# John's Machine
$ git push origin master
To john@githost:simplegit.git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'

Σε αυτήν την περίπτωση, δεν επιτράπηκε στον Τζον να ωθήσει επειδή στο μεταξύ έχει ωθήσει η Τζέσικα τις αλλαγές της. Αυτό είναι κάτι που πρέπει να καταλάβουμε καλά, αν είμαστε συνηθισμένοι στο Subversion, επειδή όπως παρατηρούμε οι δύο προγραμματιστές δεν επεξεργάστηκαν το ίδιο αρχείο. Παρόλο που το Subversion πραγματοποιεί αυτόματα μια τέτοια συγχώνευση στον διακομιστή, αν τα αρχεία που έχουν υποστεί επεξεργασία είναι διαφορετικά, στο Git πρέπει πρώτα να συγχωνεύσουμε τις υποβολές τοπικά. Με άλλα λόγια, ο Τζον πρέπει να ανακτήσει (fetch) τις αλλαγές της Τζέσικα και να τις συγχωνεύσει στο τοπικό του αποθετήριο προτού του επιτραπεί να ωθήσει:

Το πρώτο βήμα που κάνει ο Τζον είνα να ανακτήσει τη δουλειά της Τζέσικα (αυτό μόνο ανακτά τη δουλειά που ανέβασε η Τζέσικα, δεν τη συγχωνεύει ακόμα στη δουλειά του Τζον):

$ git fetch origin
...
From john@githost:simplegit
 + 049d078...fbff5bc master     -> origin/master

Σε αυτό το σημείο, το τοπικό αποθετήριο του Τζον μοιάζει με αυτό:

Το αποκλίνον ιστορικό του Τζον
Figure 57. Το αποκλίνον ιστορικό του Τζον.

Τώρα ο Τζον μπορεί να συγχωνεύσει τη δουλειά της Τζέσικα που ανέκτησε με δική του τοπική δουλειά:

$ git merge origin/master
Merge made by the 'recursive' strategy.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

Εφόσον η συγχώνευση ολοκληρωθεί ομαλά, το ιστορικό του Τζον θα μοιάζει με αυτό:

Το αποθετήριο του Τζον μετά τη συγχώνευση του `origin/master`
Figure 58. Το αποθετήριο του Τζον μετά τη συγχώνευση του origin/master

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

$ git push origin master
...
To john@githost:simplegit.git
   fbff5bc..72bbc59  master -> master

Τελικά, το ιστορικό υποβολών του Τζον θα μοιάζει με αυτό:

Ιστορικό του Τζον μετά την ώθηση στον διακομιστή `origin`
Figure 59. Ιστορικό του Τζον μετά την ώθηση στον διακομιστή origin

Στο μεταξύ, η Τζέσικα εργαζόταν πάνω σε έναν θεματικό κλάδο που ονόμασε issue54 και έκανε τρεις υποβολές σε αυτόν. Δεν έχει ακόμη ανακτήσει τις αλλαγές του Τζον, επομένως το ιστορικό υποβολών της μοιάζει ως εξής:

Θεματικός κλάδος της Τζέσικα
Figure 60. Θεματικός κλάδος της Τζέσικα

Ξαφνικά, η Τζέσικα μαθαίνει ότι ο Τζον ώθησε νέα δουλειά στον διακομιστή και θέλει να της ρίξει μια ματιά, οπότε ανακτά (fetch) όλα τα δεδομένα που δεν έχει, από τον διακομιστή:

# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master

Έτσι, ανακτά τη δουλειά που έχει ωθήσει ο Τζον στο μεταξύ. To ιστορικό της Τζέσικα μοιάζει τώρα με το εξής:

Ιστορικό της Τζέσικα μετά την ανάκτηση των αλλαγών του Τζον
Figure 61. Ιστορικό της Τζέσικα μετά την ανάκτηση των αλλαγών του Τζον

Η Τζέσικα θεωρεί ότι ο τοπικός της κλάδος είναι έτοιμος, αλλά θέλει να γνωρίζει ποιο μέρος της δουλειάς του Τζον πρέπει να συγχωνεύσει στη δική της δουλειά, ώστε να ωθήσει. Εκτελεί την εντολή git log για να το μάθει:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date:   Fri May 29 16:01:27 2009 -0700

   Remove invalid default value

Η σύνταξη issue54..origin/master είναι ένα φίλτρο της εντολής log που ζητά από το Git να εμφανίσει μόνο τον κατάλογο των υποβολών που βρίσκονται στον τελευταίο κλάδο (στη συγκεκριμένη περίπτωση τον origin/master) που δεν βρίσκονται στον πρώτο κλάδο (στη συγκεκριμένη περίπτωση “issue54”). Θα εξετάσουμε λεπτομερώς σε αυτή τη σύνταξη στην ενότητα Εύρος υποβολών.

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

Τώρα, η Τζέσικα μπορεί να συγχωνεύσει τον θεματικό της κλάδο στον δικό της master, να συγχωνεύσει τη δουλειά του Τζον (origin/master) στον δικό της κλάδο master και στη συνέχεια να ωθήσει ξανά στον διακομιστή.

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

$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

Μπορεί να συγχωνεύσει πρώτα είτε τον κλάδο origin/master είτε τον issue54 — και οι δύο είναι upstream, οπότε η σειρά δεν έχει σημασία. Το τελικό στιγμιότυπο θα είναι πανομοιότυπο ανεξάρτητα από τη σειρά που επιλέγει· μόνο το ιστορικό θα είναι ελαφρά διαφορετικό. Επιλέγει να συγχωνεύσει πρώτα στον issue54:

$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 files changed, 6 insertions(+), 1 deletions(-)

Δεν παρουσιάζονται προβλήματα· όπως μπορούμε να δούμε ήταν μια απλή συγχώνευση ταχυπροώθησης. Τώρα η Τζέσικα ολοκληρώνει τη διαδικασία της τοπικής συγχώνευσης, συγχωνεύοντας την δουλειά του Τζον που κάθεται στον κλάδο origin/master:

$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

Τα πάντα συγχωνεύονται καθαρά και το ιστορικό της Τζέσικα μοιάζει με αυτό:

Το ιστορικό της Τζέσικα μετά τη συγχώνευση των αλλαγών του Τζον
Figure 62. Το ιστορικό της Τζέσικα μετά τη συγχώνευση των αλλαγών του Τζον

Τώρα, ο κλάδος origin/master είναι προσβάσιμος από τον κλάδο master της Τζέσικα, οπότε θα πρέπει να είναι σε θέση να ωθήσει με επιτυχία (υποθέτοντας ότι ο Τζον δεν ώθησε ξανά στο μεταξύ):

$ git push origin master
...
To jessica@githost:simplegit.git
   72bbc59..8059c15  master -> master

Κάθε προγραμματιστής έχει υποβάλλει μερικές φορές και έχει συγχωνεύσει επιτυχώς στο έργο του άλλου.

Το ιστορικό της Τζέσικα μετά την ώθηση όλων των αλλαγών στον διακομιστή
Figure 63. Το ιστορικό της Τζέσικα μετά την ώθηση όλων των αλλαγών στον διακομιστή.

Αυτή είναι μία από τις πιο απλές ροές εργασίας. Εργαζόμαστε για κάποιο χρονικό διάστημα (συνήθως σε έναν θεματικό κλάδο) και συγχωνεύουμε τον κύριο κλάδο μας όταν είναι έτοιμος να ενσωματωθεί. Όταν θέλουμε να κοινοποιήσουμε αυτή τη δουλειά, ανακτούμε και συγχωνεύουμε από το origin/master στο master αν αυτός έχει αλλάξει και τον, και τελικά ωθούμε τον κλάδο μας master στον διακομιστή. Η συνήθης ακολουθία γεγονότων είναι κάτι σαν:

Γενική ακολουθία γεγονότων για μια απλή ροή εργασίας με πολλούς προγραμματιστές
Figure 64. Γενική ακολουθία γεγονότων για μια απλή ροή εργασίας με πολλούς προγραμματιστές

Ιδιωτική ομάδα με διαχειριστή

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

Ας πούμε ότι ο Τζον και η Τζέσικα δουλεύουν μαζί σε μια λειτουργικότητα (ας την ονομάσουμε “featureA”) ενώ η Τζέσικα και η Τζόσι εργάζονται σε μια άλλη (ας πούμε, “featureB”). Σε αυτή την περίπτωση, η εταιρεία χρησιμοποιεί ένα είδος ροής εργασίας με διαχειριστή ενσωμάτωσης, στην οποία η εργασία των μεμονωμένων ομάδων ενσωματώνεται μόνο από ορισμένους μηχανικούς και ο κλάδος master του κύριου αποθετηρίου μπορεί να ενημερωθεί μόνο από αυτούς τους μηχανικούς. Σε αυτό το σενάριο όλη η δουλειά γίνεται σε κλάδους κοινούς σε ομάδες και συγκεντρώνονται αργότερα από τους υπεύθυνους ενσωμάτωσης.

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

# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'Add limit to log function'
[featureA 3300904] Add limit to log function
 1 files changed, 1 insertions(+), 1 deletions(-)

Σε αυτό το σημείο, πρέπει να κοινοποιήσει τη δουλειά της με τον Τζον, οπότε ωθεί τις υποβολές του κλάδου της featureA στον διακομιστή. Η Τζέσικα δεν έχει πρόσβαση ώθησης στον κλάδο master — μόνον οι μηχανικοί ενσωμάτωσης έχουν — γι' αυτό πρέπει να ωθήσει σε έναν άλλο κλάδο για να συνεργαστεί με τον Τζον:

$ git push -u origin featureA
...
To jessica@githost:simplegit.git
 * [new branch]      featureA -> featureA

Η Τζέσικα ειδοποιεί τον Τζον για να του πει ότι έχει ωθήσει μέρος της δουλειάς της σε έναν κλάδο που ονομάζεται featureA και μπορεί τώρα να τον δει. Ενώ περιμένει τα σχόλια του Τζον, η Τζέσικα αποφασίζει να αρχίσει να δουλεύει στη λειτουργικότητα featureB με την Τζόσι. Για να ξεκινήσει, ξεκινά έναν νέο κλάδο, με βάση τον κλάδο master του διακομιστή:

# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'

Τώρα, η Τζέσικα κάνει δύο υποβολές στον κλάδο featureB:

$ vim lib/simplegit.rb
$ git commit -am 'Make ls-tree function recursive'
[featureB e5b0fdc] Make ls-tree function recursive
 1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'Add ls-files'
[featureB 8512791] Add ls-files
 1 files changed, 5 insertions(+), 0 deletions(-)

Το αποθετήριο της Τζέσικα μοιάζει με αυτό:

Αρχικό ιστορικό υποβολών της Τζέσικα
Figure 65. Αρχικό ιστορικό υποβολών της Τζέσικα

Είναι έτοιμη να ωθήσει τη δουλειά της, αλλά λαμβάνει ένα e-mail από την Τζόσι ότι ένας κλάδος με κάποια αρχική εργασία σε αυτήν τη λειτουργικότητα έχει ήδη ωθηθεί στον διακομιστή ως featureBee. Η Τζέσικα πρέπει πρώτα να συγχωνεύσει αυτές τις αλλαγές με τις δικές της, προτού να μπορέσει να ωθήσει στον διακομιστή. Πρώτα ανακτά τις αλλαγές της Τζόσι με git fetch:

$ git fetch origin
...
From jessica@githost:simplegit
 * [new branch]      featureBee -> origin/featureBee

Με την προϋπόθεση ότι η Τζέσικα βρίσκεται ακόμα στον κλάδο της featureB, μπορεί να συγχωνεύσει τη δουλειά της Τζόσι με git merge:

$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
 lib/simplegit.rb |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

Σε αυτό το σημείο, η Τζέσικα θέλει να ωθήσει όλο αυτόν τον συγχωνευμένο κλάδο “featureB” στον διακομιστή, αλλά δεν θέλει απλά να ωθήσει τον δικό της κλάδο featureB. Αντίθετα, αφού η Τζόσι έχει ήδη ξεκινήσει έναν upstream κλάδο featureBee, η Τζέσικα θέλει να ωθήσει σε αυτόν τον κλάδο, το οποίο και κάνει με την εντολή:

$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee

Αυτό ονομάζεται refspec. Μια πιο λεπτομερής συζήτηση για τα refspec του Git και διάφορα πράγματα που μπορούμε να κάνουμε με αυτά υπάρχουν στην ενότητα Τα refspec. Παρατηρούμε επίσης τη σημαία -u· είναι συντομογραφία του --set-upstream, που ρυθμίζει τους κλάδους για ευκολότερη ώθηση και ελκυσμό (pulling) αργότερα.

Ξάφνου, η Τζέσικα παίρνει ένα e-mail από τον Τζον, που της λέει ότι έχει ωθήσει κάποιες αλλαγές στον κλάδο featureA στον οποίο συνεργάζονται και της ζητά να τις ρίξει μια ματιά. Και πάλι, η Τζέσικα εκτελεί ένα git fetch για να ανακτήσει ό,τι νέο υλικό υπάρχει στον διακομιστή, συμπεριλμβανομένων φυσικά των αλλαγών του Τζον:

$ git fetch origin
...
From jessica@githost:simplegit
   3300904..aad881d  featureA   -> origin/featureA

Η Τζέσικα μπορεί να δει το log της δουλειάς του Τζον συγκρίνοντας το περιεχόμενο του ενημερωμένου κλάδου featureA με το τοπικό της αντίγραφο του ίδιου κλάδου:

$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date:   Fri May 29 19:57:33 2009 -0700

    Increase log output to 30 from 25

Εφόσον της αρέσει αυτό που βλέπει μπορεί να συγχωνεύσει τη δουλειά του Τζον στον δικό της τοπικό κλάδο featureA:

$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
 lib/simplegit.rb |   10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

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

$ git commit -am 'Add small tweak to merged content'
[featureA 774b3ed] Add small tweak to merged content
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
   3300904..774b3ed  featureA -> featureA

Το ιστορικό υποβολών της Τζέσικα τώρα μοιάζει κάτι τέτοιο:

Ιστορικό της Τζέσικα μετά την υποβολή σε έναν θεματικό κλάδο
Figure 66. Ιστορικό της Τζέσικα μετά την υποβολή σε έναν θεματικό κλάδο

Κάποια στιγμή, οι Τζέσικα, Τζόσι και Τζον ενημερώνουν τους διαχειριστές ενσωμάτωσης ότι οι κλάδοι featureA και featureBee στον διακομιστή είναι έτοιμοι για ενσωμάτωση στον κύριο κλάδο. Αφού οι διαχειριστές ενσωμάτωσης συγχωνεύσουν αυτούς τους κλάδους στον κύριο κλάδο, μία ανάκτηση θα κατεβάσει τη νέα υποβολή συγχώνευσης, κάνοντας το ιστορικό να μοιάζει με αυτό:

Ιστορικό της Τζέσικα μετά τη συγχώνευση και των δύο θεματικών κλάδων της
Figure 67. Ιστορικό της Τζέσικα μετά τη συγχώνευση και των δύο θεματικών κλάδων της

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

Βασική ακολουθία αυτής της ροής εργασίας διαχειριζόμενη ομάδα
Figure 68. Βασική ακολουθία αυτής της ροής εργασίας διαχειριζόμενη ομάδα

Αποσχισμένα δημόσια έργα

Η συνεργασία στα δημόσια έργα είναι κάπως διαφορετική. Επειδή δεν έχουμε δικαιώματα για άμεση ενημέρωση κλάδων στο έργο, θα πρέπει να στέλνουμε τη δουλειά μας στους διαχειριστές με κάποιον άλλο τρόπο. Το πρώτο παράδειγμα περιγράφει τη συνεισφορά μέσω απόσχισης (forking) σε διακομιστές Git που υποστηρίζουν την εύκολη απόσχιση. Πολλοί διακομιστές φιλοξενίας (συμπεριλαμβανομένων των GitHub, BitBucket, repo.or.cz και άλλων) και πολλοί διαχειριστές έργων αναμένουν αυτό το στυλ συνεισφοράς. Η επόμενη ενότητα ασχολείται με έργα που προτιμούν να δέχονται συνεισφορές/επιθέματα μέσω e-mail.

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

$ git clone <url>
$ cd project
$ git checkout -b featureA
  ... work ...
$ git commit
  ... work ...
$ git commit
Note

Ίσως θελήσετε να χρησιμοποιήσετε την εντολή rebase -i για να συνθλίψετε (squash) την εργασία σας σε μία μόνο υποβολή ή να αναδιατάξετε την εργασία σας στις υποβολές για να διευκολύνετε τον διαχειριστή που θα ελέγξει το επίθεμά σας — βλ. Η ιστορία ξαναγράφεται για περισσότερες πληροφορίες σχετικά με τη διαδραστική αλλαγή βάσης.

Όταν ολοκληρώσουμε την εργασία μας στον κλάδο μας και είμαστε έτοιμοι να τον επιστρέψουμε στους διαχειριστές, μεταβαίνουμε στην αρχική σελίδα του έργου και κάνουμε κλικ στο κουμπί “Fork”, ώστε να δημιουργήσουμε τη δική μας εγγράψιμη διχάλα του έργου. Στη συνέχεια, πρέπει να προσθέσουμε το URL αυτού του αποθετηρίου ως απομακρυσμένο αποθετήριο, στη συγκεκριμένη περίπτωση ας το ονομάσουμε myfork:

$ git remote add myfork <url>

Μετά πρέπει να ωθήσουμε την εργασία μας σε αυτό το αποθετήριο. Είναι ευκολότερο να ωθήσουμε τον θεματικό κλάδο στον οποίο εργαζόμαστε στο αποσχισμένο μας αποθετήριο παρά να τον συγχωνεύσουμε στον κλάδο master και να ωθήσουμε αυτόν. Ο λόγος είναι ότι εάν η εργασία δεν γίνει αποδεκτή ή γίνουν αποδεκτά μόνο κάποια τμήματά της (cherry-picked), δεν χρειάζεται να επαναφέρουμε τον κύριο κλάδο μας (η Git cherry-pick λειτουργία αναλύεται με περισσότερη λεπτομέρια στο Ροές εργασίας με αλλαγή βάσης και ανθολόγηση) Αν οι διαχειριστές συγχωνεύσουν (merge), επανατοποθετήσουν (rebase) ή επιλέξουν μόνον τμήματα της δουλειάς μας (cherry-pick), τότε κάποια στιγμή θα την πάρουμε πίσω τραβώντας την από το αποθετήριό τους:

Σε κάθε περίπτωση μπορούμε να ωθήσουμε την δουλειά μας ως εξής:

$ git push -u myfork featureA

Όταν έχουμε ωθήσει τη δουλειά μας στη διχάλα μας, πρέπει να ειδοποιήσουμε τους συντηρητές του αρχικού έργου ότι έχουμε κάνει κάποια δουειά που θέλουμε να συγχωνεύσουν. Αυτό συχνά ονομάζεται αίτημα ελκυσμού (pull request) και μπορούμε είτε να το δημιουργήσουμε μέσω της ιστοσελίδας — το GitHub έχει τον δικό του μηχανισμό “αιτημάτων ελκυσμού”, τον οποίο θα εξετάσουμε στην ενότητα GitHub — είτε να τρέξουμε την εντολή git request-pull και να στείλουμε την έξοδό της με e-mail στον διαχειριστή του έργου.

Η εντολή request-pull παίρνει τον βασικό κλάδο στον οποίο θέλουμε να ελκυστεί ο θεματικός μας κλάδος και το URL του αποθετηρίου Git από το οποίο θέλουμε να τον τραβήξουν και εξάγει μια σύνοψη όλων των αλλαγών που ζητάμε να ελκυστούν. Για παράδειγμα, αν η Τζέσικα θέλει να στείλει ένα αίτημα ελκυσμού στον Τζον και έχει κάνει δύο υποβολές στον θεματικό κλάδο που μόλις ώθησε, μπορεί να τρέξει το εξής:

$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jessica Smith (1):
        Create new function

are available in the git repository at:

  https://githost/simplegit.git featureA

Jessica Smith (2):
      Add limit to log function
      Increase log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

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

Σε ένα έργο στο οποίο δεν είμαστε ο συντηρητής, είναι γενικά ευκολότερο να έχουμε έναν κλάδο όπως ο master να παρακολουθεί πάντα τον origin/master και να δουλεύουμε σε θεματικούς κλάδους τους οποίους μπορούμε εύκολα να διαγράψουμε αν απορριφθούν. Η διατήρηση ξεχωριστών θεματικών κλάδων για διαφορετικά θέματα διευκολύνει επίσης την αλλαγή βάσης της εργασίας μας, αν η άκρη του κύριου αποθετηρίου έχει μετακινηθεί εν τω μεταξύ και οι υποβολές μας δεν εφαρμόζονται πλέον με καθαρό τρόπο. Για παράδειγμα, εάν θέλουμε να δημιουργήσουμε ένα δεύτερο θέμα εργασίας στο έργο, καλό είναι να μην συνεχίζουμε να εργαζόμαστε στον κλάδο του θέματος το οποίο μόλις ωθήσαμε, αλλά να ξεκινήσουμε από τον κλάδο master του αποθετηρίου:

$ git checkout -b featureB origin/master
  ... work ...
$ git commit
$ git push myfork featureB
$ git request-pull origin/master myfork
  ... email generated request pull to maintainer ...
$ git fetch origin

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

Αρχικό ιστορικό υποβολών με τη δουλειά από το `featureB`
Figure 69. Αρχικό ιστορικό υποβολών με τη δουλειά από το featureB

Ας υποθέσουμε ότι ο συντηρητής του έργου έχει τραβήξει κάμποσα επιθέματα και έχει δοκιμάσει τον πρώτο μας κλάδο, αλλά δεν μπορεί να τον συγχωνεύσει χωρίς συγκρούσεις. Σε αυτή την περίπτωση, μπορούμε να προσπαθήσουμε να αλλάξουμε τη βάση (rebase) αυτού του κλάδου στην κορυφή του κλάδου origin/master, να επιλύσουμε τις συγκρούσεις για τον διαχειριστή και στη συνέχεια να ωθήσουμε εκ νέου τις αλλαγές μας:

$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA

Αυτό επανεγγράφει το ιστορικό μας, ώστε τώρα μοιάζει με το Ιστορικό υποβολών μετά από δουλειά στο featureA.

Ιστορικό υποβολών μετά από δουλειά στο `featureA`
Figure 70. Ιστορικό υποβολών μετά από δουλειά στο featureA

Επειδή αλλάξαμε τη βάση του κλάδου, πρέπει να χρησιμοποιήσουμε την επιλογή -f στην εντολή push, ώστε να μπορέσουμε να αντικαταστήσουμε τον κλάδο featureA στον διακομιστή με μια υποβολή που δεν είναι απόγονός τoυ. Μια εναλλακτική θα ήταν να ωθήσουμε αυτή τη νέα δουλειά σε ένα διαφορετικό κλάδο στον διακομιστή (ίσως με όνομα featureAv2).

Ας δούμε ένα ακόμα πιθανό σενάριο: ο διαχειριστής έχει εξετάσει την εργασία στον δεύτερό μας κλάδο και του αρέσει η ιδέα, αλλά θα ήθελε να αλλάξουμε μια-δυο λεπτομέρειες στην υλοποίησή της. Θα εκμεταλλευτούμε αυτή την ευκαιρία για να αλλάξουμε τη βάση (rebase) της δουλειά μας ώστε να διακλαδίζεται από τον τρέχοντα κλάδο master του έργου. Ξεκινάμε ένα νέο κλάδο που διακλαδίζεται από τον τρέχοντα κλάδο origin/master, συνθλίβουμε (squash) τις αλλαγές του featureB σε αυτόν, επιλύουμε τυχόν συγκρούσεις, κάνουμε τις αλλαγές στην υλοποίηση που μας ζητήθηκαν και στη συνέχεια τα ωθούμε όλα αυτά ως έναν νέο κλάδο:

$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
  ... change implementation ...
$ git commit
$ git push myfork featureBv2

Η επιλογή --squash παίρνει όλη τη δουλειά που υπάρχει στον συγχωνευμένο κλάδο και τη στριμώχνει (squashes it) σε ένα σύνολο αλλαγών ώστε παράγει μία κατάσταση του αποθετηρίου σαν να συνέβη στην πραγματικότητα μια συγχώνευση χωρίς όμως να πραγματοποιήσει υποβολή συγχώνευσης. Αυτό σημαίνει ότι η μελλοντική μας υποβολή θα έχει μόνο έναν γονέα και μας επιτρέπει να εισάγουμε όλες τις αλλαγές από κάποιον άλλο κλάδο και μετά να κάνουμε περισσότερες αλλαγές πριν καταγράψουμε τη νέα υποβολή. Επίσης, μία άλλη χρήσιμη επιλογή στην περίπτωση της προεπιλεγμένης διαδικασίας συγχώνευσης είναι η --no-commit που αναβάλλει την υποβολής συγχώνευσης.

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

Ιστορικό υποβολών μετά τη δουλειά στο `featureBv2`
Figure 71. Ιστορικό υποβολών μετά τη δουλειά στο featureBv2

Δημόσιο έργο μέσω e-mail

Πολλά έργα έχουν θεσπίσει διαδικασίες για την αποδοχή των επιθεμάτων — θα πρέπει να ελέγξουμε τους συγκεκριμένους κανόνες του έργου, επειδή διαφέρουν από έργο σε έργο. Δεδομένου ότι υπάρχουν κάποια παλαιότερα, μεγαλύτερα έργα που αποδέχονται επιθέματα μέσω mailing list προγραμματιστών, θα εξετάσουμε ένα τέτοιο παράδειγμα.

Η ροή εργασίας είναι παρόμοια με την προηγούμενη περίπτωση — δημιουργούμε θεματικούς κλάδους, έναν για κάθε σειρά επιθεμάτων στα οποία εργαζόμαστε. Η διαφορά είναι στο πως τους υποβάλλουμε στο έργο. Αντί να αποσχίσουμε μία διχάλα (fork) από το έργο και να την ωθήσουμε στη δική μας εγγράψιμη έκδοση, μπορούμε να δημιουργήσουμε εκδόσεις e-mail για κάθε σειρά υποβολών και να τις αποστείλούμε με e-mail στη mailing list προγραμματιστών:

$ git checkout -b topicA
  ... work ...
$ git commit
  ... work ...
$ git commit

Τώρα έχουμε δύο υποβολές που θέλουμε να στείλουμε στη mailing list. Χρησιμοποιούμε την git format-patch για να δημιουργήσουμε τα μορφοποιημένα αρχεία mbox που μπορούμε να στείλουμε με e-mail στη λίστα — μετατρέπει κάθε υποβολή σε μήνυμα e-mail με την πρώτη γραμμή του μηνύματος υποβολής ως θέμα και το υπόλοιπο μήνυμα συν το επίθεμα, που εισάγει η υποβολή, ως σώμα του e-mail. Η ομορφιά σε αυτό είναι ότι η εφαρμογή ενός επιθέματος από ένα μήνυμα e-mail που έχει παραχθεί με format-patch διατηρεί όλες τις πληροφορίες υποβολής όπως πρέπει.

$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch

Η εντολή format-patch εκτυπώνει τα ονόματα των αρχείων-επιθεμάτων που δημιουργεί. Ο διακόπτης -M λέει στο Git να αναζητήσει μετονομασίες. Τα αρχεία καταλήγουν να μοιάζουν ως εξής:

$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] Add limit to log function

Limit log functionality to the first 20

---
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
2.1.0

Μπορούμε επίσης να επεξεργαστούμε αυτά τα αρχεία/επιθέματα για να προσθέσουμε περισσότερες πληροφορίες για τη mailing list που δεν θέλουμε να εμφανίζονται στο μήνυμα υποβολής. Αν προσθέσουμε κείμενο μεταξύ της γραμμής --- και της αρχής του επιθέματος (τη γραμμή diff --git), τότε ναι μεν οι προγραμματιστές μπορούν να το διαβάσουν, αλλά το κείμενο αυτό αγνοείται κατά τη διαδικασία εφαρμογής του επιθέματος.

Για να στείλουμε το παραπάνω ως e-mail σε μια mailing list, μπορούμε είτε να επικολλήσουμε το αρχείο στο πρόγραμμα e-mail είτε να το στείλουμε μέσω ενός προγράμματος γραμμής εντολών. Η επικόλληση του κειμένου προκαλεί συχνά ζητήματα μορφοποίησης, ειδικά με “ευφυέστερους” πελάτες που δεν διατηρούν κατάλληλα τους χαρακτήρες αλλαγής γραμμής και άλλους λευκούς χαρακτήρες. Ευτυχώς, το Git παρέχει ένα εργαλείο που μας βοηθά να στέλνουμε κατάλληλα μορφοποιημένα επιθέματα μέσω IMAP, κάτι που ενδεχομένως μας διευκολύνει. Θα δείξουμε πως να στείλουμε επιθέματα μέσω του Gmail, που τυγχάνει να είναι το πρόγραμμα e-mail που γνωρίζουμε καλύτερα· μπορούμε να διαβάσουμε λεπτομερείς οδηγίες για κάποια προγράμματα e-mail στο τέλος του προαναφερθέντος αρχείου Documentation/SubmittingPatches στον πηγαίο κώδικα του Git.

Πρώτα, πρέπει να ρυθμίσουμε την ενότητα imap στο αρχείο ~/.gitconfig. Μπορούμε να ορίσουμε ξεχωριστά κάθε τιμή με μια ακολουθία εντολών git config ή μπορούμε να τις προσθέσουμε χειροκίνητα· όπως και να 'χει τελικά το αρχείο config θα πρέπει να φαίνεται σαν αυτό:

[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = YX]8g76G_2^sFbd
  port = 993
  sslverify = false

Αν ο διακομιστής IMAP δεν χρησιμοποιεί SSL, οι δύο τελευταίες γραμμές πιθανώς δεν είναι απαραίτητες και η τιμή host θα είναι imap:// αντί για imaps://. Όταν έχει ρυθμιστεί το αρχείο ~/.gitconfig, μπορούμε να εκτελέσουμε την εντολή git imap-send για να τοποθετήσουμε τη σειρά επιθεμάτων στον φάκελο Drafts του καθορισμένου διακομιστή IMAP:

$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done

Σε αυτό το σημείο, θα πρέπει να μπορούμε να μεταβούμε στον φάκελο Drafts, να αλλάξουμε το πεδίο To στη mailing list στην οποία θα αποστείλούμε την ενημερωμένη έκδοση κώδικα, ενδεχομένως να κοινοποιήσουμε με CC στον υπεύθυνο συντήρησης ή όποιον είναι υπεύθυνος για αυτή την ενότητα και να το αποστείλουμε.

Μπορούμε επίσης να στείλουμε τα επιθέματα μέσω ενός διακομιστή SMTP. Όπως και πριν, μπορούμε να ορίσουμε ξεχωριστά κάθε τιμή με μια σειρά εντολών git config ή μπορούμε να τις προσθέσουμε χειροκίνητα στην ενότητα sendemail στο αρχείο ~/.gitconfig:

[sendemail]
  smtpencryption = tls
  smtpserver = smtp.gmail.com
  smtpuser = user@gmail.com
  smtpserverport = 587

Μετά από αυτό, μπορούμε να χρησιμοποιήσουμε το git send-email για να αποστείλουμε τα επιθέματά μας:

$ git send-email *.patch
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y

Στη συνέχεια, το Git εκτοξεύει κάμποσες πληροφορίες log που μοιάζουν με κάτι σαν το παρακάτω για κάθε επίθεμα που στέλνουμε:

(mbox) Adding cc: Jessica Smith <jessica@example.com> from
  \line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] Add limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>

Result: OK
Tip

Για βοήθεια όσον αφορά στην παραμετροποίηση του συστήματός σας και του e-mail, περισσότερες συμβουλές και κόλπα και ένα χώρο πειραματισμού για δομικές αποστολής επιθεμάτων με e-mail, επισκεφτείτε το git-send-email.io.

Ανακεφαλαίωση

Σε αυτή την ενότητα καλύψαμε διάφορες ροές εργασίας και μιλήσαμε για τις διαφορές ανάμεσα στην εργασία ως μέλος κάποιας μικρής ομάδας σε έργα κλειστού κώδικα και την εργασία σε ένα μεγάλο δημόσιο έργο. Γνωρίζουμε πως να ελέγχουμε σφάλματα διαστημάτων πριν να υποβάλουμε αλλαγές και να γράφουμε εξαιρετικά μηνύματα υποβολής. Μάθαμε πως να μορφοποιούμε επιθέματα και να τα στέλνουμε με email σε μια mailing list προγραμματιστών. Καλύψαμε επίσης τις συγχωνεύσεις στο πλαίσιο των διαφορετικών ροών εργασίας. Πλέον είμαστε καλά προετοιμασμένοι να συνεργαστούμε με άλλους σε κάποιο έργο.

Στη συνέχεια, θα δούμε πως εργαζόμαστε στην άλλη όψη του νομίσματος: τη συντήρηση ενός έργου Git. Θα μάθουμε πως να είμαστε καλοπροαίρετοι δικτάτορες ή διαχειριστές ενσωμάτωσης.