Git

2.2 Τα θεμελιώδη στοιχεία του Git - Καταγραφή αλλαγών στο αποθετήριο

Καταγραφή αλλαγών στο αποθετήριο

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

Θυμηθείτε ότι κάθε αρχείο στον κατάλογο που εργάζεστε μπορεί να βρίσκεται σε δύο καταστάσεις: παρακολουθούμενο (tracked) ή μη-παρακολουθούμενο. Τα παρακολουθούμενα αρχεία είναι αυτά που βρίσκονταν στο τελευταίο στιγμιότυπο καθώς και αρχεία που μόλις έχουν τοποθετηθεί στην και μπορεί να είναι τροποποιημένα, ατροποποίητα ή ευρισκόμενα στον προθάλαμο (staged). Τα μη-παρακολουθούμενα αρχεία είναι όλα τα υπόλοιπα — τα αρχεία στον κατάλογο εργασίας που δεν βρίσκονταν στο τελευταίο στιγμιότυπο, και δεν βρίσκονται ούτε στον προθάλαμο. Όταν κλωνοποιείτε για πρώτη φορά ένα αποθετήριο, όλα τα αρχεία θα είναι παρακολουθούμενα και ατροποποίητα επειδή το Git μόλις τα έχει κάνει ανασύρει (check out) και δεν τα έχετε επεξεργαστεί ακόμα.

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

Ο κύκλος ζωής της κατάστασης των αρχείων σας.
Figure 8. Ο κύκλος ζωής της κατάστασης των αρχείων σας.

Έλεγχος της κατάστασης των αρχείων σας

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

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

Αυτό σημαίνει ότι έχετε ένα καθαρό κατάλογο εργασίας· με άλλα λόγια, κανένα από τα παρακολουθούμενα αρχεία σας δεν έχουν τροποποιηθεί. Επίσης το Git δεν βλέπει κανένα μη-παρακολουθούμενο αρχείο, αλλιώς θα τα το Git θα τα παρέθετε στο παραπάνω μήνυμα. Τέλος, η εντολή αυτή σας ενημερώνει σε ποιον κλάδο βρίσκεστε καθώς και ότι δεν έχει αποκλίνει από τον αντίστοιχο κλάδο που βρίσκεται στον διακομιστή. Προς το παρόν ο κλάδος αυτός είναι ο master, που είναι και ο προεπιλεγμένος. Η ενότητα [ch03-git-branching] θα εξετάσει πιο αναλυτικά τους κλάδους και τις αναφορές.

Note

Το GitHub άλλαξε το προεπιλεγμένο όνομα κλάδου από master σε main στα μέσα του 2020, κάτι που μιμήθηκαν και άλλοι διακομιστές Git. Συνεπώς, ενδεχομένως θα δείτε ότι το προεπιλεμγένο όνομα κλάδου σε κάποια πιο καινούρια αποθετήρια είναι main και όχι master. Επιπλέον, το προεπιλεγμένο όνομα βρόχου μπορεί να τροποποιηθεί (όπως είδατε στην ενότητα [_new_default_branch]), συνεπώς ίσως δείτε κάποιο άλλο όνομα για τον προεπιλεγμένο κλάδο.

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

Ας υποθέσουμε ότι έχετε προσθέσει ένα νέο αρχείο στο έργο σας, ένα απλό αρχείο README. Αν το αρχείο αυτό δεν προϋπήρχε και εκτελέσετε την εντολή git status, θα δείτε το μη-παρακολουθούμενο αρχείο σας ως εξής:

$ echo 'My Project' > README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    README

nothing added to commit but untracked files present (use "git add" to track)

Καταλαβαίνετε ότι το αρχείο README είναι μη-παρακολουθούμενο διότι βρίσκεται κάτω από τον τίτλο "Untracked files". Μη-παρακολουθούμενο ουσιαστικά σημαίνει ότι το Git βλέπει ένα αρχείο το οποίο δεν υπήρχε στο προηγούμενο στιγμιότυπο (υποβολή, commit) και που δεν έχει τοποθετηθεί ακόμα στον προθάλαμο· Το Git δεν θα συμπεριλάβει το αρχείο αυτό στα επόμενα στιγμιότυπα που θα υποβάλλετε, αν δεν το ζητήσετε ρητά. Αυτό γίνεται ώστε να μην συμπεριλάβετε κατά λάθος στο έργο σας αρχεία τα οποία δεν θέλατε να συμπεριλάβετε, για παράδειγμα εκτελέσιμα αρχεία. Σε αυτή την περίπτωση, θέλετε να συμπεριλάβετε το αρχείο README στο έργο σας, οπότε ας ξεκινήσουμε να παρακολουθείτε το αρχείο .

Παρακολούθηση νέων αρχείων

Για να αρχίσετε να παρακολουθείτε ένα καινούριο αρχείο, χρησιμοποιείτε την εντολή git add. Για να αρχίσετε να παρακολουθείτε το αρχείο READΜE, γράψτε:

$ git add README

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

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README

Καταλαβαίνετε ότι το αρχείο αυτό πλέον έχει τοποθετηθεί στον προθάλαμο διότι βρίσκεται κάτω από τον τίτλο Changes to be committed. Αν σε αυτό το σημείο υποβάλετε τα αρχεία σας, η έκδοση του αρχείου README που θα αποθηκευτεί στο στιγμιότυπο θα είναι αυτή που υπήρχε όταν εκτελέσατε την εντολή git add. Ίσως θυμάστε ότι προηγουμένως κάνατε κάτι αντίστοιχο, εκτελέσατε την εντολή git init ακολουθούμενη από git add <files> — αυτό το κάνατε για να αρχίσετε να παρακολουθείτε τα αρχεία του καταλόγου. Η εντολή git add μπορεί να ακολουθείται είτε από ένα αρχείο είτε από έναν κατάλογο. Αν ακολουθείται από κατάλογο τότε η εντολή θα καταχωρήσει όλα τα αρχεία του συγκεκριμένου καταλόγου.

Καταχώριση τροποποιημένων αρχείων στον προθάλαμο

Ας αλλάξουμε ένα αρχείο που βρίσκεται ήδη υπό παρακολούθηση. Αν αλλάξετε το ήδη παρακολουθούμενο αρχείο CONTRIBUTING.md και εκτελέσετε την εντολή git status ξανά, θα δείτε κάτι σαν το εξής:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Το αρχείο CONTRIBUTING.md βρίσκεται κάτω από την κατηγορία Changed but not staged for commit, που σημαίνει ότι ένα αρχείο υπό παρακολούθηση έχει τροποποιηθεί στον κατάλογο εργασίας, αλλά δεν έχει τοποθετηθεί ακόμα στον προθάλαμο. Για να το τοποθετήσετε στον προθάλαμο θα πρέπει να εκτελέσετε την εντολή git add. Η εντολή git add έχει πολλές λειτουργίες· τη χρησιμοποιείτε για να ξεκινήσετε την παρακολούθηση καινούριων αρχείων, για να τοποθετήσετε αρχεία στον προθάλαμο αλλά και για άλλες λειτουργίες όπως το να επισημάνετε αρχεία που προέρχονται από συγκρούσεις συγχώνευσης (merge conflicts) ως επιλυμένα. Μπορείτε να σκεφτείτε την εντολή ως "πρόσθεσε αυτό το περιεχόμενο στην επόμενη υποβολή" αντί για "πρόσθεσε αυτό το αρχείο στο έργο". Ας εκτελέσουμε την εντολή git add, ώστε να καταχωρήσετε το αρχείο CONTRIBUTING.md και έπειτα ας τρέξουμε την εντολή git status ξανά:

$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Και τα δύο αρχεία πλέον βρίσκονται στον προθάλαμα και θα συμπεριληφθούν στην επόμενη υποβολή στιγμιοτύπου. Ας υποθέσουμε τώρα ότι θυμηθήκατε άλλη μία μικρή αλλαγή που θέλετε να κάνετε στο αρχείο CONTRIBUTING.md πριν το υποβάλλετε. Το ανοίγετε ξανά, κάνετε την αλλαγή που θέλετε και είστε έτοιμοι για την υποβολή. Παρόλα αυτά ας εκτελέσουμε git status για άλλη μια φορά:

$ vim CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Τι στην ευχή συμβαίνει; Το αρχείο CONTRIBUTING.md εμφανίζεται και ως αρχείο τοποθετημένο στον προθάλαμο, αλλά και ως αρχείο που δεν έχει τοποθετηθεί στον προθάλαμο. Πώς είναι αυτό δυνατόν; Αυτό που συμβαίνει είναι ότι το Git τοποθετεί στον προθάλαμο ένα αρχείο ακριβώς όπως είναι τη στιγμή που εκτελείτε την εντολή git add. Αν υποβάλλετε το στιγμιότυπο τώρα, η έκδοση του αρχείου CONTRIBUTING.md που θα συμπεριληφθεί στην υποβολή είναι αυτή που υπήρχε όταν εκτελέσατε την εντολή git add (και όχι η τωρινή έκδοση του αρχείου). Γενικά, αν τροποποιήσετε ένα αρχείο αφότου έχετε εκτελέσει την εντολή git add, θα πρέπει να εκτελέσετε git add ξανά, αν θέλετε να τοποθετήσετε την τελευταία εκδοχή του αρχείου στον προθάλαμο:

$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

Σύντομη κατάσταση

Αν και η εντολή git status δίνει αρκετά ολοκληρωμένες πληροφορίες, οι πληροφορίες αυτές είναι λίγο φλύαρες. Το Git διαθέτει μια σημαία για πιο συνοπτική περιγραφή της κατάστασης των αλλαγών. Αν εκτελέσετε git status -s ή git status --short θα έχετε ένα πιο απλοποιημένο αποτέλεσμα.

$ git status -s
 M README
MM Rakefile
A  lib/git.rb
M  lib/simplegit.rb
?? LICENSE.txt

Τα καινούργια αρχεία που δεν παρακολουθούνται ακόμα ακολουθούν ένα ??, τα καινούρια αρχεία που έχουν καταχωρηθεί με A, τα τροποποιημένα αρχεία με M κ.ο.κ. Το αποτέλεσμα της εντολής περιλαμβάνει δύο στήλες για το κάθε αρχείο. Η αριστερή στήλη περιγράφει την κατάσταση του αρχείου στον προθάλαμο και η δεξιά στήλη την κατάστασή του στο δέντρο εργασίας. ότι το αρχείο έχει καταχωρηθεί και το δεξί ότι έχει τροποποιηθεί. Στο παραπάνω παράδειγμα, το αρχείο README έχει τροποποιηθεί στον κατάλογο εργασίας, αλλά δεν έχει ακόμα τοποθετηθεί στον προθάλαμο, ενώ το αρχείο lib/simplegit.rb είναι και τροποποιημένο και έχει τοποθετηθεί στον προθάλαμο. Το αρχείο Rakefile από την άλλη έχει τροποποιηθεί, τοποθετηθεί στον προθάλαμο και στη συνέχεια τροποποιήθηκε ξανά, που σημαίνει ότι υπάρχουν κάποιες αλλαγές που έχουν τοποθετηθεί στον προθάλαμο και άλλες που δεν έχουν τοποθετηθεί στον προθάλαμο.

Αγνόηση αρχεία

Συμβαίνει συχνά, να υπάρχει μια κατηγορία αρχείων που δεν θέλετε να τα προσθέτετε στον προθάλαμο, ούτε καν να φαίνονται ως μη-παρακολουθούμενα. Αυτά είναι συνήθως αρχεία που δημιουργούνται αυτόματα όπως αρχεία .log ή αρχεία που δημιουργούνται κατά τη μεταγλώττιση. Σε αυτές τις περιπτώσεις μπορείτε να δημιουργήσετε ένα αρχείο με όνομα .gitignore, στο οποίο να καταγράψετε τα μοτίβα των ονομάτων αυτών των αρχείων. Να ένα παράδειγμα αρχείου .gitignore:

$ cat .gitignore
*.[oa]
*~

Η πρώτη γραμμή λέει στο Git να αγνοεί όλα τα αρχεία που τελειώνουν σε .o ή .a — αρχεία που είναι συνήθως αποτέλεσμα της μεταγλώττισης τους κώδικά σας. Η δεύτερη γραμμή λέει στο Git να αγνοεί όλα τα αρχεία που τελειώνουν με τον χαρακτήρα ~, το οποίο χρησιμοποιείται από πολλούς επεξεργαστές κειμένου, όπως ο Emacs, για να δηλώσει τα προσωρινά αρχεία. Μπορείτε επίσης να συμπεριλάβετε καταλόγους που περιλαμβάνουν αρχεία καταγραφής, προσωρινούς καταλόγους κ.ο.κ. Γενικά είναι καλή ιδέα να ρυθμίσετε το αρχείο .gitignore νωρίς ώστε να μην υποβάλλετε κατά λάθος αρχεία που δεν θέλετε να βρίσκονται στο αποθετήριό σας.

Οι κανόνες για τα μοτίβα που μπορείτε να δηλώσετε στο αρχείο .gitignore είναι οι εξής:

  • Οι κενές γραμμές ή οι γραμμές που ξεκινούν με # αγνοούνται.

  • Μπορείτε να χρησιμοποιήσετε τα κλασικά μοτίβα για ονόματα αρχείων (glob patterns) και αυτά εφαρμόζονται αναδρομικά.

  • Μπορείτε να ξεκινήσετε τα μοτίβα σας με slash (/) ώστε να αποφύγετε την αναδρομικότητα

  • Μπορείτε να τελειώσετε τα μοτίβα σας με slash (/) ώστε να ορίσετε έναν κατάλογο.

  • Μπορείτε να αντιστρέψετε ένα μοτίβο χρησιμοποιώντας ένα θαυμαστικό (!) στην αρχή του.

Τα μοτίβα αυτά μοιάζουν με απλοποιημένες κανονικές εκφράσεις (regular expressions), σαν αυτές που χρησιμοποιούν τα λειτουργικά συστήματα. Ένας αστερίσκος (*) αντιστοιχεί σε μηδέν ή περισσότερους χαρακτήρες· το [abc] αντιστοιχεί σε οποιονδήποτε χαρακτήρα βρίσκεται μέσα στις αγκύλες (σε αυτή την περίπτωση a, b και c· το σύμβολο του αγγλικού ερωτηματικού (?) αντιστοιχεί σε έναν και μόνο χαρακτήρα· και οι αγκύλες που περιέχουν χαρακτήρες που διαχωρίζονται με παύλα ([0-9]) αντιστοιχίζονται σε όλους τους χαρακτήρες που υπάρχουν μεταξύ τους (σε αυτή την περίπτωση, όλους τους αριθμούς από το 0 μέχρι το 9). Μπορείτε επίσης να χρησιμοποιήσετε δύο αστερίσκους για να αντιστοιχίσετε εμφωλευμένους καταλόγους· η έκφραση a/**/z αντιστοιχεί στους καταλόγους a/z, a/b/z, a/b/c/z κ.ο.κ.

Ορίστε άλλο ένα παράδειγμα ενός αρχείου .gitignore:

# αγνόησε όλα τα αρχεία .a
*.a

# αλλά να παρακολουθείς το lib.a, παρά το ότι αγνοείς τα αρχεία .a
!lib.a

# αγνόησε μόνο το αρχείο TODO στον τρέχοντα κατάλογο, όχι το subdir/TODO
/TODO

# αγνόησε όλα τα αρχεία σε οποιονδήποτε κατάλογο με όνομα build
build/

# αγνόησε το doc/notes.txt, αλλά όχι το doc/server/arch.txt
doc/*.txt

# αγνόησε όλα τα αρχεία .pdf στον φάκελο doc/ και όλους τους υποφακέλους του
doc/**/*.pdf
Tip

Αν θέλετε κάποια παραδείγματα για να ξεκινήσετε, το GitHub διατηρεί μια λίστα με παραδείγματαα αρχείων .gitignore για πολλές γλώσσες προγραμματισμού στη διεύθυνση https://github.com/github/gitignore.

Note

Στην απλούστερη περίπτωση, ένα αποθετήριο έχει μόνο ένα αρχείο .gitignore στον κατάλογο root, το οποίο εφαρμόζεται αναδρομικά σε όλο το αποθετήριο. Όμως είναι δυνατό να έχετε επιπρόσθετα αρχεία .gitignore σε υποφακέλους. Οι κανόνες σε αυτά τα εμφωλευμένα αρχεία .gitignore εφαρμόζονται μόνο σε αρχεία του φακέλου στον οποίο βρίσκονται. Το αποθετήριο με τον πηγαίο κώδικα του πυρήνα του Linux έχει 206 αρχεία .gitignore.

Περισσότερες λεπτομέρειες σχετικά με πολλαπλά αρχεία .gitignore είναι πέρα από τους σκοπούς αυτού του βιβλίου· για περισσότερα δείτε το man gitignore.

Προβολή των καταχωρημένων και μη-καταχωρημένων αλλαγών

Αν η εντολή git status είναι πολύ αόριστη για σας και θέλετε να δείτε ακριβώς τι έχετε αλλάξει (και όχι μόνο ποια αρχεία έχουν αλλάξει), μπορείτε να χρησιμοποιήσετε την εντολή git diff. Θα καλύψουμε την εντολή αυτή πιο αναλυτικά αργότερα, αλλά το πιθανότερο είναι ότι θα τη χρησιμοποιείτε πιο συχνά για να απαντήσετε αυτές τις δύο ερωτήσεις: Τι έχετε αλλάξει και δεν έχετε ακόμα τοποθετήσει στον προθάλαμο; Και τι έχετε τοποθετήσει στον προθάλαμο και είναι έτοιμο να υποβληθεί; Ενώ η εντολή git status απαντά σε αυτές τις ερωτήσεις πολύ γενικά, απαριθμώντας τα ονόματα των αρχείων, η εντολή git diff θα σας δείξει ακριβώς ποιες γραμμές προστέθηκαν ή αφαιρέθηκαν — με άλλα λόγια το επίθεμα (patch).

Έστω λοιπόν ότι έχετε επεξεργαστεί και τοποθετήσει στον προθάλαμο το αρχείο README ξανά και μετά επεξεργάζεστε το αρχείο CONTRIBUTING.md χωρίς να το τοποθετήσετε στον προθάλαμο. Αν τώρα εκτελέσετε την εντολή git status, θα δείτε κάτι τέτοιο:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

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

$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
 Please include a nice description of your changes when you submit your PR;
 if we have to read the whole diff to figure out why you're contributing
 in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.

 If you are starting to work on a particular area, feel free to submit a PR
 that highlights your work in progress (and note in the PR title that it's

Η εντολή αυτή συγκρίνει τον κατάλογο εργασίας σας με ό,τι υπάρχει στον προθάλαμο. Σας λέει τις αλλαγές που έχετε κάνει, αλλά δεν έχετε ακόμα τοποθετήσει στον προθάλαμο.

Αν θέλετε να δείτε τι έχετε τοποθετήσει στον προθάλαμο, που θα είναι και μέρος της επόμενης υποβολής, μπορείτε να χρησιμοποιήσετε την εντολή git diff --staged. Η εντολή αυτή συγκρίνει τις αλλαγές που βρίσκονται στον προθάλαμο με την τελευταία υποβολή:

$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project

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

Ας δείτε άλλο ένα παράδειγμα. Έστω ότι έχετε τοποθετήσει το αρχείο CONTRIBUTING.md στον προθάλαμο και έπειτα το έχετε τροποποιήσει. Τότε μπορείτε να χρησιμοποιήσετε την εντολή git diff για να δείτε ποιες ακριβώς αλλαγές του αρχείου έχουν τοποθετηθεί στον προθάλαμο και ποιες όχι. Ας υποθέσουμε ότι το περιβάλλον εργασίας σας είναι κάπως έτσι:

$ git add CONTRIBUTING.md
$ echo '# test line' >> CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   CONTRIBUTING.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Τότε μπορείτε να χρησιμοποιήσετε την εντολή git diff για να δείτε τι δεν έχει τοποθετηθεί ακόμα στον προθάλαμο

$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 643e24f..87f08c8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -119,3 +119,4 @@ at the
 ## Starter Projects

 See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).
+# test line

καθώς και την εντολή git diff --cached για να δείτε τι έχετε τοποθετήσει στον προθάλαμο μέχρι στιγμής (τα --staged και --cached είναι συνώνυμα):

$ git diff --cached
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
 Please include a nice description of your changes when you submit your PR;
 if we have to read the whole diff to figure out why you're contributing
 in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.

 If you are starting to work on a particular area, feel free to submit a PR
 that highlights your work in progress (and note in the PR title that it's
Note
Χρήση της git diff από εξωτερικό πρόγραμμα

Θα συνεχίσουμε να χρησιμοποιούμε την εντολή git diff με διάφορους τρόπους στο βιβλίο. Αν όμως προτιμάτε να βλέπετε τις διαφορές μεταξύ των αρχείων με κάποιο γραφικό εργαλείο (και όχι μέσα από τη γραμμή εντολών), υπάρχει και άλλος τρόπος. Αν εκτελέσετε την εντολή git difftool αντί για git diff μπορείτε να δείτε τις διαφορές των αρχείων με προγράμματα όπως τα emerge, vimdiff και άλλα (συμπεριλαμβανομένων και εμπορικών λογισμικών). Εκτελέστε την εντολή git difftool --tool-help για να δείτε ποια προγράμματα είναι διαθέσιμα στο σύστημά σας.

Υποβολή των αλλαγών

Τώρα που ο προθάλαμός σας περιέχει τις αλλαγές που θέλετε, είστε έτοιμοι να τις υποβάλλετε (commit). Θυμηθείτε ότι όλα τα μη καταχωρημένα αρχεία, δηλαδή όσα αρχεία έχετε δημιουργήσει ή τροποποιήσει και για τα οποία δεν εκτελέσατε την εντολή git add, δεν θα συμπεριληφθούν σε αυτή την υποβολή και θα παραμείνουν ως τροποποιημένα αρχεία στον δίσκο σας. Σε αυτή την περίπτωση, έστω ότι την τελευταία φορά που εκτελέσατε την εντολή git status, είδατε ότι τα πάντα είχαν τοποθετηθεί στον προθάλαμο και συνεπώς είστε έτοιμοι να υποβάλλετε τις αλλαγές σας. Ο απλούστερος τρόπος για να υποβάλλετε αλλαγές είναι να πληκτρολογήσετε git commit:

$ git commit

Όταν το κάνετε, θα εκκινήσετε τον προεπιλεγμένο επεξεργαστή κειμένου σας. Αυτός είναι καθορισμένος από τη μεταβλητή περιβάλλοντος (environment variable) της γραμμής εντολών, $EDITOR, και συνήθως είναι ο vim ή ο emacs, αλλά μπορείτε να χρησιμοποιήσετε την εντολή git config --global core.editor ώστε να χρησιμοποιήσετε τον επεξεργαστή κειμένου της αρεσκείας σας, όπως είδατε στο Ξεκινώντας με το Git.

Ο επεξεργαστής κειμένου σας θα εμφανίσει το παρακάτω κείμενο (αυτό το παράδειγμα είναι οθόνη του Vim):

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#	new file:   README
#	modified:   CONTRIBUTING.md
#
~
~
~
".git/COMMIT_EDITMSG" 9L, 283C

Βλέπετε ότι το προεπιλεγμένο μήνυμα υποβολής περιέχει το τελευταίο αποτέλεσμα της εντολής git status μέσα σε σχόλια και μια κενή γραμμή στην αρχή. Μπορείτε να αφαιρέσετε τα σχόλια αυτά και να γράψετε το δικό σας μήνυμα υποβολής ή να τα αφήσετε ως έχουν ώστε να σας βοηθήσουν αργότερα να θυμηθείτε ποια αρχεία υποβάλλετε.

Note

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

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

Εναλλακτικά, μπορείτε να γράψετε το μήνυμα υποβολής σας μαζί με την εντολή commit, μετά τη σημαία -m ως εξής:

$ git commit -m "Story 182: fix benchmarks for speed"
[master 463dc4f] Story 182: fix benchmarks for speed
 2 files changed, 2 insertions(+)
 create mode 100644 README

Μόλις κάνατε την πρώτη σας υποβολή! Βλέπετε ότι η υποβολή αυτή σας έχει δώσει κάποιες πληροφορίες: τον κλάδο στον οποίο υποβάλλατε τις αλλαγές σας (master), το άθροισμα ελέγχου SHA-1 (SHA-1 checksum) της υποβολής (463dc4f), πόσα αρχεία τροποποιήθηκαν, καθώς και στατιστικά για το πόσες γραμμές προστέθηκαν και αφαιρέθηκαν σε αυτή την υποβολή.

Θυμηθείτε ότι η υποβολή αλλαγών καταγράφει το στιγμιότυπο το οποίο είχατε εκείνη τη στιγμή στον προθάλαμο. Οτιδήποτε δεν είχατε τοποθετήσει στον προθάλαμο, παραμένει εκεί τροποποιημένο και μπορείτε να το υποβάλλετε αργότερα με άλλο ένα commit. Κάθε φορά που πραγματοποιείτε μια υποβολή, καταγράφετε ένα στιγμιότυπο του έργου σας, στο οποίο μπορείτε να επανέλθετε αργότερα ή να το συγκρίνετε με κάποιο μελλοντικό στιγμιότυπο του έργου σας.

Παραλείποντας τον προθάλαμο

Παρόλο που ο προθάλαμος είναι πολύ χρήσιμος για να κόβετε και ράβετε τις υποβολές σας όπως ακριβώς θέλετε, ενίοτε είναι πιο περίπλοκος από όσο χρειάζεστε να είναι στην εργασία σας. Αν θέλετε να παραλείψετε τον προθάλαμο, το Git παρέχει μια απλή συντόμευση. Αν προσθέσετε την επιλογή -a στην εντολή git commit αναγκάζετε το Git να τοποθετεί αυτόματα όλα τα αρχεία υπό παρακολούθηση πριν κάνει το commit, επιτρέποντάς σας έτσι να παραλείψετε την εντολή git add:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'Add new benchmarks'
[master 83e38c7] Add new benchmarks
 1 file changed, 5 insertions(+), 0 deletions(-)

Παρατηρείτε ότι στην περίπτωση αυτή, δεν έχετε εκτελέσει την εντολή git add για το αρχείο CONTRIBUTING.md πριν υποβάλετε το στιγμιότυπό σας επειδή η σημαία -a περιλαμβάνει όλα τα αρχεία που έχουν τροποποιηθεί. Αυτό είναι βολικό, αλλά χρειάζεται προσοχή· με αυτή η σημαία μπορεί μερικές φορές να συμπεριλάβτε αλλαγές που δεν θέλατε να υποβάλετε.

Διαγραφή αρχείων

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

Αν απλά διαγράψετε το αρχείο από τον κατάλογο εργασίας σας, θα εμφανίζεται κάτω από την κατηγορία Changed but not updated (unstaged, που ουσιαστικά σημαίνει ότι δεν έχει τοποθετηθεί στον προθάλαμο) του αποτελέσματος της εντολής git status:

$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    PROJECTS.md

no changes added to commit (use "git add" and/or "git commit -a")

Αν στη συνέχεια εκτελέσετε την εντολή git rm, η αλλαγή αυτή, δηλαδή η διαγραφή του αρχείου, θα τοποθετηθεί στον προθάλαμο:

$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    PROJECTS.md

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

Κάτι άλλο που μπορεί να θέλετε να κάνετε, είναι να κρατήσετε το αρχείο στον κατάλογο εργασίας σας, αλλά να το αφαιρέσετε από τον προθάλαμο. Με άλλα λόγια, ίσως θέλετε να κρατήσετε το αρχείο στον σκληρό σας δίσκο, αλλά να μην βρίσκεται πλέον υπό παρακολούθηση από το Git. Αυτό είναι ιδιαίτερα χρήσιμο αν είχατε ξεχάσει να προσθέσετε κάτι στο αρχείο .gitignore και το τοποθετήσατε στον προθάλαμο κατά λάθος, όπως για παράδειγμα μεγάλα αρχεία .log ή αρχεία .a poy προέκυψαν από μεταγλώττιση. Για να το κάνετε αυτό, χρησιμοποιείτε την επιλογή --cached:

$ git rm --cached README

Μπορείτε να χρησιμοποιήσετε την παραπάνω εντολή με αρχεία, καταλόγους και μοτίβα glob αρχείων. Αυτό σημαίνει ότι μπορείτε να εκτελέσετε εντολές όπως:

$ git rm log/\*.log

Παρατηρήστε το backslash (\) μπροστά από τον αστερίσκο, *. Είναι απαραίτητο, επειδή το Git χρησιμοποιεί κι αυτό ανάπτυξη των ονομάτων των αρχείων (file name expansion), επιπρόσθετα με την ανάπτυξη των ονομάτων των αρχείων του κελύφους. Η παραπάνω εντολή αφαιρεί όλα τα αρχεία που έχουν την κατάληξη .log στον κατάλογο log/. Επίσης, θα μπορούσατε να κάνετε κάτι τέτοιο:

$ git rm \*~

Η εντολή αυτή αφαιρεί όλα τα αρχεία που τελειώνουν με τον χαρακτήρα ~.

Μετακίνηση αρχείων

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

Κατ' αυτή την έννοια, το γεγονός ότι το Git έχει εντολή mv μπορεί να δημιουργήσει σύγχυση. Αν θέλετε να μετονομάσετε ένα αρχείο στο Git, μπορείτε να το κάνετε κάπως έτσι

$ git mv file_from file_to

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

$ git mv README.md README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    ----

Η εντολή αυτή όμως, είναι ισοδύναμη με το να εκτελέσετε το εξής:

[source,console]

$ mv README.md README $ git rm README.md $ git add README

Το Git καταλαβαίνει ότι ουσιαστικά πρόκειται για μετονομασία, οπότε δεν έχει σημασία αν μετονομάσετε ένα αρχείο με αυτό τον τρόπο ή με την εντολή `mv`.
Η μόνη πραγματική διαφορά είναι ότι η εντολή `mv` είναι μία εντολή αντί για τρεις, άρα πιο βολική.
Και το πιο σημαντικό, μπορείτε να χρησιμοποιήσετε όποιο εργαλείο θέλετε για να μετονομάσετε ένα αρχείο και να λύσετε το πρόβλημα της προσθήκης/διαγραφής, `add`/`rm`, του αρχείου αργότερα, πριν την υποβολή.


[[r_viewing_history]]
=== Χρησιμοποιώντας το ιστορικό υποβολών

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

Τα παρακάτω παραδείγματα χρησιμοποιούν ένα πολύ απλό έργο που ονομάζετε `simplegit`.
Για να αποκτήσετε το έργο, μπορείτε να εκτελέσετε:

[source,console]
Αν εκτελέσετε την εντολή `git log` σε αυτό το έργο, θα πάρετε κάτι σαν το εξής:(((εντολές git, log)))

[source,console]

$ git log commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700

Change version number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700

Remove unnecessary test

commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 10:31:28 2008 -0700

Initial commit
Εξ ορισμού, η εντολή `git log` παραθέτει όλες τις υποβολές που έχουν γίνει στο αποθετήριο σε αντίστροφη χρονολογική σειρά (οι πιο πρόσφατες υποβολές εμφανίζονται πρώτες).
Όπως μπορείτε να δείτε, η εντολή καταγράφει κάθε υποβολή μαζί με το άθροισμα ελέγχου SHA-1, το όνομα και το e-mail του δημιουργού της, την ημερομηνία εγραφής, καθώς και το μήνυμα της υποβολής.

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

Μια από τις πιο χρήσιμες επιλογές είναι η `-p` ή `patch`, η οποία δείχνει τη διαφορά που εισήχθη σε κάθε υποβολή.
Μπορείτε επίσης να περιορίσετε το πλήθος των υποβολών, για παράδειγμα χρησιμοποιήστε την `-2`, για να δείτε μόνο τις δύο τελευταίες υποβολές:

[source,console]

$ git log -p -2 commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700

Change version number

diff --git a/Rakefile b/Rakefile index a874b73..8f94139 100644 --- a/Rakefile + b/Rakefile @@ -5,7 +5,7 @@ require rake/gempackagetask spec = Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = "simplegit" - s.version = "0.1.0" + s.version = "0.1.1" s.author = "Scott Chacon" s.email = "schacon@gee-mail.com" s.summary = "A simple gem for using Git in Ruby code."

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700

Remove unnecessary test

diff --git a/lib/simplegit.rb b/lib/simplegit.rb index a0a60ae..47c6340 100644 --- a/lib/simplegit.rb + b/lib/simplegit.rb @@ -18,8 +18,3 @@ class SimpleGit end

 end
-
-if $0 == __FILE__
-  git = SimpleGit.new
-  puts git.show
-end
Η επιλογή αυτή εμφανίζει τις ίδιες πληροφορίες, αλλά κάθε υποβολή ακολουθείται και από τις διαφορές (diff) που εισήγαγε.
Αυτό είναι πολύ χρήσιμο στην περίπτωση που θέλετε να εξετάσετε κάποιον κώδικα ή για να δείτε στα γρήγορα τι έγινε σε μια ακολουθία υποβολών που εισήγαγε κάποιος συνεργάτης σας.
Μπορείτε επίσης να χρησιμοποιήσετε επιλογές ανακεφαλαίωσης με την `git log`.
Για παράδειμα, αν θέλετε να δείτε κάποια συντομευμένα στατιστικά για την κάθε υποβολή, μπορείτε να χρησιμοποιήσετε την επιλογή `--stat`:

[source,console]

$ git log --stat commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700

Change version number
Rakefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700

Remove unnecessary test
lib/simplegit.rb | 5 -----
1 file changed, 5 deletions(-)

commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 10:31:28 2008 -0700

Initial commit
README           |  6 ++++++
Rakefile         | 23 +++++++++++++++++++++++
lib/simplegit.rb | 25 +++++++++++++++++++++++++
3 files changed, 54 insertions(+)
Όπως μπορείτε να δείτε, η επιλογή `--stat` εκτυπώνει κάτω από κάθε υποβολή, μια λίστα με τα τροποποιημένα αρχεία, πόσα αρχεία άλλαξαν, καθώς και πόσες γραμμές προστέθηκαν ή αφαιρέθηκαν σε αυτά τα αρχεία.
Επίσης, εκτυπώνει και μια περίληψη αυτών των πληροφοριών στο τέλος.

Μια ακόμα χρήσιμη επιλογή είναι η `--pretty`.
Η επιλογή αυτή αλλάζει τη μορφή της εξόδου της εντολής.
Υπάρχουν μερικές προϋπάρχουσες τιμές που μπορείτε να χρησιμοποιήσετε.
Η τιμή `oneline` εκτυπώνει κάθε υποβολή σε μία γραμμή, κάτι το οποίο μπορεί να σας φανεί χρήσιμο αν βλέπετε πολλές υποβολές.
Επιπλέον, οι τιμές `short`, `full` και `fuller` εμφανίζουν την ίδια έξοδο σε παρόμοια μορφή αλλά με λιγότερες ή περισσότερες πληροφορίες αντίστοιχα:

[source,console]

$ git log --pretty=oneline ca82a6dff817ec66f44342007202690a93763949 changed the version number 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

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

[source,console]

$ git log --pretty=format:"%h - %an, %ar : %s" ca82a6d - Scott Chacon, 6 years ago : changed the version number 085bb3b - Scott Chacon, 6 years ago : removed unnecessary test a11bef0 - Scott Chacon, 6 years ago : first commit

Ο πίνακας <<pretty_format>> παραθέτει μερικές από τις πιο χρήσιμες επιλογές μορφοποίησης.

[[pretty_format]]
.Χρήσιμες επιλογές για την `git log --pretty=format`
[cols="1,4",options="header"]
|================================
| Επιλογή  | Περιγραφή εξόδου
| `%H`     | Αριθμός SHA-1 υποβολής
| `%h`     | Συντμημένος αριθμός SHA-1 υποβολής
| `%T`     | Αριθμός SHA-1 δέντρου
| `%t`     | Συντμημένος αριθμός SHA-1 δέντρου
| `%P`     | Αριθμοί SHA-1 γονέων
| `%p`     | Συντμημένοι αριθμός SHA-1 γονέων
| `%an`    | Όνομα συγγραφέα
| `%ae`    | E-mail συγγραφέα
| `%ad`    | Ημερομηνία συγγραφέα (σε μορφή που ορίζεται από την επιλογή `--date=`)
| `%ar`    | Ημερομηνία συγγραφέα, σχετική
| `%cn`    | Όνομα υποβάλλοντος
| `%ce`    | E-mail υποβάλλοντος
| `%cd`    | Ημερομηνία υποβολής
| `%cr`    | Ημερομηνία υποβολής, σχετική
| `%s`     | Θέμα
|================================

Ίσως αναρωτιέστε ποια είναι η διαφορά μεταξύ του _author_ (δημιουργού, συγγραφέα) και του _committer_ (αυτού που έκανε την υποβολή).
Ο δημιουργός είναι το πρόσωπο που έγραψε αρχικά τη δουλειά, ενώ o _committer_ είναι αυτός που την υπέβαλε τελευταίος.
Συνεπώς, αν στείλετε ένα επίθεμα για ένα έργο και κάποιος άλλος το υποβάλλει, θα πρέπει και οι δύο να πιστωθείτε τη δουλειά: εσείς ως δημιουργός και ο άλλος ως αυτός που την υπέβαλλε.
Θα αναλύσουμε αυτή τη διαφορά αυτή σε λίγο, στην ενότητα <<ch05-distributed-git>>.

Οι επιλογές `oneline` και `format` είναι ιδιαίτερα χρήσιμες σε συνδυασμό με μια άλλη επιλογή της εντολής `log`, την `--graph`.
Η επιλογή αυτή προσθέτει ένα μικρό γράφημα με χαρακτήρες ASCII που δείχνει το ιστορικό των κλάδων και των συγχωνεύσεων:

[source,console]

$ git log --pretty=format:"%h %s" --graph * 2d3acf9 Ignore errors from SIGCHLD on trap * 5e3ee11 Merge branch master of https://github.com/dustin/grit.git |\ | * 420eac9 Add method for getting the current branch * | 30e367c Timeout code and tests * | 5a09431 Add timeout protection to grit * | e1193f8 Support for heads with slashes in them |/ * d6016bc Require time for xmlschema * 11d191e Merge branch defunkt into local

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

Όλες αυτές είναι μερικές απλές επιλογές με τις οποίες μπορείτε να μορφοποιήσετε την έξοδο της εντολής `git log` -- υπάρχουν και πολλές άλλες.
Ο πίνακας <<log_options>> καταγράφει όλες τις επιλογές που καλύψαμε μέχρι στιγμής, καθώς και κάποιες άλλες επιλογές μορφοποίησης που μπορεί να σας φανούν χρήσιμες μαζί με μια περιγραφή του τρόπου με τον οποίο αλλάζουν το αποτέλεμσμα της εντολής `log`.

[[log_options]]
.Συνήθεις επιλογές για την `git log`
[cols="1,4",options="header"]
|================================
| Επιλογή           | Περιγραφή
| `-p`              | Δείξε το επίθεμα (patch) που εισήχθηκε σε κάθε υποβολή.
| `--stat`          | Δείξε στατιστικά σχετικά με τα αρχεία που τροποποιήθηκαν σε κάθε υποβολή.
| `--shortstat`     | Δείξε μόνον την τελευταία γραμμή από την επιλογή `--stat`, που δείχνει μόνο τον συνολικό αριθμό αρχείων που τροποποιήθηκαν και αριθμό γραμμών που προστέθηκαν και αφαιρέθηκαν.
| `--name-only`     | Δείξε τη λίστα των αρχείων που τροποποιήθηκαν (μετά τις πληροφορίες για την υποβολή).
| `--name-status`   | Δείξε επιπλέον τη λίστα των αρχείων που επηρεάστηκαν με προσθήκη/τροποποίηση/διαγραφή πληροφοριών.
| `--abbrev-commit` | Δείξε μόνο τους πρώτους χαρακτήρες από τους 40 του αθροίσματος ελέγχου SHA-1.
| `--relative-date` | Δείξε τη σχετική ημερομηνία σε σχετική μορφή (π.χ. "`2 weeks ago`") αντί για την πλήρη.
| `--graph`         | Δείξε ένα γράφημα ASCII του κλάδου και του ιστορικού συγχώνευσης δίπλα στην έξοδο του μητρώου.
| `--pretty`        | Δείξε τις υποβολές σε εναλλακτική μορφή· οι επιλογές είναι: `oneline`, `short`, `full`, `fuller` και `format` (στην οποία ορίζετε τη δική σας μορφή).
|================================

==== Περιορίζοντας την έξοδο της `log`

Εκτός από τις επιλογές μορφοποίησης, η εντολή `git log` έχει και πολλές επιλογές που περιορίζουν την έξοδό της -- δηλαδή, επιλογές που σας δείχνουν μόνο ένα υποσύνολο των συνολικών υποβολών.
Έχετε ήδη δει μια τέτοια επιλογή, την `-2`, η οποία εμφανίζει μόνο τις δύο τελευταίες υποβολές.
Μάλιστα, μπορείτε να χρησιμοποιήσετε `-<n>`, όπου `n` είναι ένας ακέραιος που αντιστοιχεί στις τελευταίες `n` υποβολές.
Στην πραγματικότητα, βέβαια, είναι σχετικά απίθανο να χρησιμοποιείτε αυτή την επιλογή συχνά, καθώς το Git εκ προεπιλογής παροχετεύει την έξοδο σε έναν σελιδοποιητή οπότε βλέπετε μόνο μια σελίδα με τα στοιχεία του μητρώου κάθε φορά.

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

[source,console]

$ git log --since=2.weeks

Η εντολή αυτή χρησιμοποιείται με πολλές διαφορετικές μορφές.
Μπορείτε να προσδιορίσετε για παράδειγμα μια συγκεκριμένη μέρα, `"2008-01-15"`, ή μια σχετική μέρα όπως `"2 years 1 day 3 minutes ago"`.

Μπορείτε επίσης να φιλτράρετε τη λίστα με τις υποβολές με βάση κάποια κριτήρια.
Η επιλογή `--author` σάς επιτρέπει να φιλτράρετε με βάση έναν συγκεκριμένο δημιουργό.
Η επιλογή `--grep` σας επιτρέπει να ψάξετε για λέξεις-κλειδιά στα μηνύματα των υποβολών.

[NOTE]
====
Μπορείτε να χρησιμοποιήσετε περισσότερες από μία φορές τα κριτήρια αναζήτησης `--author` και `--grep`, κάτι που θα περιορίσει την έξοδο της εντολής `log` σε υποβολές που συμφωνούν με _οποιοδήποτε_ από τα μοτίβα για τον `--author` και _οποιοδήποτε_ από τα μοτίβα του `--grep`· πάντως αν προσθέσετε επιπλέον την επιλογή `--all-match`, θα περιορίσετε την έξοδο σε αυτές τις υποβολές που συμφωνούν με _όλα_ τα μοτίβα του `--grep`.
====

Ένα ακόμα πολύ χρήσιμο φίλτρο είναι η επιλογή `-S` (κατά το κοινώς λεγόμενο η "`αξίνα`" ("`pickaxe`") του Git) η οποία παίρνει μια συμβολοσειρά και σας δείχνει μόνο τις υποβολές που εισήγαγαν κάποια αλλαγή στον κώδικα, η οποία προσέθεσε ή αφαίρεσε αυτή τη συμβολοσειρά.
Για παράδειγμα, αν θέλετε να βρείτε την τελευταία υποβολή που προσέθεσε ή αφαίρεσε μια αναφορά σε μια συγκεκριμένη συνάρτηση, θα γράφατε:

[source,console]

$ git log -S <όνομα-συνάρτησης>

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

[source,console]

$ git log — path/to/file

Στον πίνακα <<limit_options>> καταγράφουμε κάποιες από αυτές τις επιλογές αυτές για εύκολη αναφορά.

[[limit_options]]
.Επιλογές που περιορίζουν την έξοδο της `git log`
[cols="2,4",options="header"]
|================================
| Επιλογή               | Περιγραφή
| `-(n)`                | Δείξε μόνον τις τελευταίες n υποβολές.
| `--since`, `--after`  | Περιόρισε τις υποβολές σε αυτές που έγιναν μετά από συγκεκριμένη ημερομηνία.
| `--until`, `--before` | Περιόρισε τις υποβολές σε αυτές που έγιναν πριν από συγκεκριμένη ημερομηνία.
| `--author`            | Δείξε μόνο τις υποβολές στις οποίες το πεδίο author συμφωνεί με συγκεκριμένη συμβολοσειρά.
| `--committer`         | Δείξε μόνο τις υποβολές στις οποίες το πεδίο committer συμφωνεί με συγκεκριμένη συμβολοσειρά.
| `--grep`              | Δείξε μόνο τις υποβολές στις οποίες το μήνυμα υποβολής περιέχει συγκεκριμένη συμβολοσειρά.
| `-S`                  | Δείξε μόνο τις υποβολές στις οποίες προστέθηκε ή αφαιρέθηκε κώδικας που ταιριάζει με συγκεκριμένη συμβολοσειρά.
|================================

Για παράδειγμα, αν θέλετε να δείτε ποιες υποβολές τροποποίησαν αρχεία τεστ στο ιστορικό του πηγαίου κώδικα του Git από τον Junio Hamano και δεν αποτελούσαν υποβολές συγχώνευσης κατά τον Οκτώβριο του 2008, μπορείτε να εκτελέσετε κάτι τέτοιο:(((log, φιλτράρισμα)))

[source,console]

$ git log --pretty="%h - %s" --author=Junio C Hamano --since="2008-10-01" \ --before="2008-11-01" --no-merges — t/ 5610e3b - Fix testcase failure when extended attributes are in use acd3b9e - Enhance hold_lock_file_for_{update,append}() API f563754 - demonstrate breakage of detached checkout with symbolic link HEAD d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths 51a94af - Fix "checkout --track -b newbranch" on detached HEAD b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch

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

[TIP]
.Αποτροπή της εμφάνισης των υποβολών συγχώνευσης
====
Ανάλογα με τη ροή εργασίας που χρησιμοποιείται στο αποθετήριό σας, ενδέχεται ένα σημαντικό ποσοστό των υποβολών στο ιστορικό να είναι απλά υποβολές συγχώνευσης, οι οποίες γενικά δεν εμπεριέχουν πολλές πληροφορίες.
Για να αποτρέψετε την εμφάνιση των υποβολών συγχώνευσης, απλά προσθέστε την επιλογή `--no-merges`.
====

[[r_undoing]]
=== Αναιρέσεις (undoing)

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

Μια συχνή αναίρεση που χρησιμοποιείται είναι η περίπτωση κατά την οποία υποβάλλετε κάτι πολύ νωρίς και ενδεχομένως ξεχάσατε να προσθέσετε κάποια αρχεία ή κάνατε κάποιο σφάλμα στο μήνυμα υποβολής.
Αν θέλετε να ξανακάνετε τη συγκεκριμένη υποβολή, να προσθέσετε τις αλλαγές που ξεχάσατε, να τις βάλετε στον προθάλαμο και να τις ξαναϋποβάλλετε, θα πρέπει να χρησιμοποιήσετε την επιλογή `--amend`:

[source,console]

$ git commit --amend

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

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

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

[source,console]

$ git commit -m initial commit $ git add forgotten_file $ git commit --amend

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

[NOTE]
====
Είναι σημαντικό να καταλάβετε ότι όταν τροποποιείτε (amend) την τελευταία σας υποβολή, στην πραγματικότητα δεν την _τροποποιείτε_ αλλά την _αντικαθιστάτε_ με μια εντελώς νέα, βελτιωμένη υποβολή που διώχνει την παλιά υποβολή και βάζει την νέα υποβολή στη θέση της.
Ουσιαστικά, είναι σαν η προηγούμενη υποβολή να μη συνέβη ποτέ και δεν θα εμφανίζετα στο ιστορικό του αποθετηρίου.

Η αξία της τροποποίησης των υποβολών είναι ότι κάνετε ελάσονες βελτιώσεις στην τελευταία σας υποβολή, χωρίς να μπουκώνετε το ιστορικό του αποθετηρίου σας με μηνύματα υποβολής της μορφής "`Ωχ, ξέχασα να προσθέσω ένα αρχείο`" ή "`Να πάρει, διόρθωσα ένα τυπογραφικό στην προηγούμενη υποβολή`".
====

[NOTE]
====
Να τροποποιείτε (amend) υποβολές που υπάρχουν μόνο τοπικα και δεν τις έχετε ωθήσει πουθενά.
Η τροποποίηση υποβολών που έχουν ήδη ωθηθεί και η ώθησή του κλάδου θα δημουργήσει προβλήματα στους συνεργάτες σας.
Περισσότερες λεπτομέρειες για το τι συμβαίνει ότι κάνετε κάτι τέτοιο, και πώς να το διορθώσετε όταν είστε αυτός που την πατάει υπάρχουν στο <<_rebase_peril>>.
====

[[r_unstaging]]
==== Αφαίρεση αρχείου από τον προθάλαμο

Στις επόμενες δύο ενότητες θα δείτε πώς μπορείτε να διαχειριστείτε τις αλλαγές που έχουν γίνει στον προθάλαμο και στον κατάλογο εργασίας.
Το καλό είναι ότι η εντολή που χρησιμοποιείτε για να προσδιορίσετε την κατάσταση αυτών των δύο περιοχών, σας υπενθυμίζει και πώς να αναιρέσετε τις αλλαγές σε αυτές τις περιοχές.
Για παράδειγμα, έστω ότι έχετε κάνει αλλαγές σε δύο αρχεία και θέλετε να τα υποβάλλετε ως ξεχωριστές αλλαγές, αλλά τα υποβάλατε κατά λάθος και τα δύο με την εντολή `git add *`.
Πώς μπορείτε να αναιρέσετε την καταχώριση του ενός από τα δύο;
Η εντολή `git status` σας υπενθυμίζει:

[source,console]

$ git add * $ git status On branch master Changes to be committed: (use "git reset HEAD <file>…​" to unstage)

renamed:    README.md -> README
modified:   CONTRIBUTING.md
Ακριβώς κάτω από το "`Changes to be committed`", σας λέει να εκτελέσετε `git reset HEAD <file>...` ώστε να αφαιρέσετε την αλλαγή από τον προθάλαμο.
Ας χρησιμοποιήσουμε λοιπόν αυτή τη συμβουλή αυτή για το αρχείο `CONTRIBUTING.md`:

[source,console]

$ git reset HEAD CONTRIBUTING.md Unstaged changes after reset: M CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>…​" to unstage)

renamed:    README.md -> README

Changes not staged for commit: (use "git add <file>…​" to update what will be committed) (use "git checkout — <file>…​" to discard changes in working directory)

modified:   CONTRIBUTING.md
Η εντολή φαίνεται λίγο περίεργη, αλλά κάνει τη δουλειά.
Το αρχείο `CONTRIBUTING.md` είναι τροποποιημένο, αλλά δεν βρίσκεται στον προθάλαμο.

[NOTE]
=====
Πράγματι, η εντολή `git reset` μπορεί να είναι επικίνδυνη, ιδιαίτερα αν την καλέσετε με τη σημαία `--hard`.
Όμως στο συγκεκριμένο σενάριο, το αρχείο στον κατάλογο εργασίας σας δεν τροποποιείται, οπότε είναι σχετικά ασφαλές.
Η εκτέλεση της εντολής `git reset` χωρίς επιπλέον επιλογές δεν είναι επικίνδυνη, τροποποιεί μόνο τον προθάλαμο.
=====

Προς το παρόν, το μόνο που χρειάζεται να γνωρίζετε για την εντολή `git reset` είναι η παραπάνω χρήση της.
Θα μπείτε σε περισσότερες λεπτομέρειες για το τι κάνει η εντολή `reset` και πώς να την κατακτήσετε, ώστε να κάνετε πραγματικά ενδιαφέροντα πράγματα στην ενότητα <<r_git_reset>>.

==== Αναίρεση τροποποίησης ενός αρχείου

Τι μπορείτε να κάνετε όμως αν διαπιστώσετε ότι δεν θέλετε να κρατήσετε τις αλλαγές που κάνατε στο αρχείο `CONTRIBUTING.md`;
Πώς μπορείτε να το ξετροποποιήσετε εύκολα -- να το φέρετε στη μορφή που είχε στην τελευταία του υποβολή (ή όπως ήταν όταν το κλωνοποιήσατε ή όπως το φέρατε στον κατάλογο εργασίας σας);
Ευτυχώς, η εντολή `git status` σας λέει πώς να το κάνετε.
Στο αποτέλεσμα του προηγούμενου παραδείγματος, η περιοχή με τα μη καταχωρημένα αρχεία είναι κάπως έτσι:

[source,console]

Changes not staged for commit: (use "git add <file>…​" to update what will be committed) (use "git restore <file>…​" to discard changes in working directory) modified: CONTRIBUTING.md

Η εντολή σας πληροφορεί ρητά πώς να απορρίψετε τις αλλαγές που έχετε κάνει.
Ας κάνουμε ό,τι λέει:

[source,console]

$ git restore CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git restore --staged <file>…​" to unstage) renamed: README.md → README

Μπορείτε να δείτε πλέον ότι οι αλλαγές σας έχουν απορριφθεί.

[IMPORTANT]
=====
Είναι σημαντικό να καταλάβετε ότι η εντολή `git restore -- <αρχείο>` είναι αρκετά επικίνδυνη.
Όλες οι αλλαγές που έχετε κάνει τοπικά σε αυτό το αρχείο έχουν πλέον χαθεί -- το Git αντικατέστησε αυτό το αρχείο με την τελευταία έκδοσή του από τον προθάλαμο ή την υποβεβλημένη καθώς έχετε αντιγράψει ένα αρχείο πάνω από αυτό.
Μην χρησιμοποιείτε αυτή την εντολή παρά μόνο αν είστε απολύτως σίγουροι ότι δεν θέλετε να κρατήσετε αυτές τις μη αποθηκευμένες τοπικές αλλαγές.
=====

<!---
Αν θέλετε να κρατήσετε τις αλλαγές που κάνατε στο αρχείο, αλλά παρόλα αυτά χρειάζεστε να το κάνετε στην άκρη, θα πρέπει να εξετάσετε τη φύλαξη των αλλαγών (stashing) και τη διακλάδωση (branching) στο κεφάλαιο <<ch03-git-branching>>.

Οτιδήποτε έχει υποβληθεί στο Git μπορεί να ανακτηθεί.
Μπορείτε να ανακτήσετε ακόμα και υποβολές σε κλάδους που έχουν διαγραφεί ή υποβολές που επανεγγράφηκαν, με την εντολή `git commit --amend` (βλ. <<r_data_recovery>> για περισσότερα σχετικά με την ανάκτηση δεδομένων).
Ωστόσο, αν κάτι δεν είναι υποβεβλημένο και το χάσετε, είναι πολύ πιθανό να μην μπορέσετε να το ανακτήσετε.
-->

[[r_remote_repos]]
=== Δουλεύοντας με απομακρυσμένα αποθετήρια

Για να μπορείτε να συνεργάζεστε σε έργα του Git, θα πρέπει να γνωρίζετε πώς να διαχειρίζεστε τα απομακρυσμένα αποθετήριά σας.
Τα απομακρυσμένα αποθετήρια (remote repositories) είναι εκδόσεις του έργου σας που βρίσκονται στο Διαδίκτυο ή σε κάποιο δίκτυο.
Μπορείτε να δημιουργήσετε όσα θέλετε και καθένα από αυτά είναι προσβάσιμα από σας είτε ως για ανάγνωση-μόνο είτε για ανάγνωση/εγγραφή.
Η συνεργασία με άλλους συμπεριλαμβάνει τη διαχείριση αυτών των απομακρυσμένων αποθετηρίων, ώθηση και ελκυσμό δεδομένων προς και από αυτά όποτε χρειάζεται να κοινοποιήσετε τη δουλειά σας ή να ενημερωθείτε για τη δουλειά άλλων.
Η διαχείριση τέτοιων αποθετηρίων χρειάζεται ικανότητες όπως: πρόσθεση καινούριων απομακρυσμένων αποθετηρίων, διαγραφή αποθετηρίων που δεν έχουν πια κάποια χρησιμότητα, διαχείριση απομακρυσμένων κλάδων και ορισμό τους ως υπο-παρακολούθηση ή τερματισμό της παρακολούθησής τους και άλλα.
Σε αυτή την ενότητα, θα ασχοληθείτε με κάποιες από αυτές τις δεξιότητες.

[NOTE]
.Τα απομακρυσμένα αποθετήρια μπορεί να βρίσκονται στον υπολογιστή σας.
====
Είναι απολύτως δυνατό να εργάζεστε με ένα "`απομακρυσμένο`" αποθετήριο, που βρίσκεται στον ίδιο υπολογιστή στον οποίο βρίσκεστε και εσείς.
Η λέξη "`απομακρυσμένο`" δεν υπονοεί απαραίτητα ότι το αποθετήριο βρίσκεται σε κάπου αλλού στο δίκτυο ή στο Διαδίκτυο, αλλά μόνο ότι βρίσκεται κάπου αλλού.
Όταν εργάζεστε με ένα τέτοιο απομακρυσμένο αποθετήριο περιλαμβάνει επίσης τις συνήθεις λειτουργίες ώθησης (push), ελκυσμού (pull) και ανάκτησης (fetch), όπως με κάθε άλλο απομακρυσμένο αποθετήριο.
====

==== Εμφάνιση των απομακρυσμένων αποθετηρίων σας

Για να δείτε τους απομακρυσμένους διακομιστές που έχετε παραμετροποιήσει, εκτελέστε την εντολή `git remote`.(((εντολές git, remote)))
Η εντολή αυτή θα σας επιστρέψει μια λίστα με τα ονόματα των απομακρυσμένων που έχετε ορίσει.
Αν έχετε κλωνοποιήσει το αποθετήριό σας, θα πρέπει να βλέπετε τουλάχιστον το `origin` -- που είναι το προεπιλεγμένο όνομα που δίνει το Git στον διακομιστή από τον οποίο μόλις κλωνοποιήσατε:

[source,console]

$ git clone https://github.com/schacon/ticgit Cloning into ticgit…​ remote: Reusing existing pack: 1857, done. remote: Total 1857 (delta 0), reused 0 (delta 0) Receiving objects: 100% (1857/1857), 374.35 KiB | 268.00 KiB/s, done. Resolving deltas: 100% (772/772), done. Checking connectivity…​ done. $ cd ticgit $ git remote origin

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

[source,console]

$ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push)

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

[source,console]

$ cd grit $ git remote -v bakkdoor https://github.com/bakkdoor/grit (fetch) bakkdoor https://github.com/bakkdoor/grit (push) cho45 https://github.com/cho45/grit (fetch) cho45 https://github.com/cho45/grit (push) defunkt https://github.com/defunkt/grit (fetch) defunkt https://github.com/defunkt/grit (push) koke git://github.com/koke/grit.git (fetch) koke git://github.com/koke/grit.git (push) origin git@github.com:mojombo/grit.git (fetch) origin git@github.com:mojombo/grit.git (push)

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

Παρατηρήστε ότι τα απομακρυσμένα αποθετήρια χρησιμοποιούν πολλά πρωτόκολλα·
θα καλύψετε αναλυτικά τα πρωτόκολλα αυτά στο <<r_git_on_the_server>>.

==== Προσθήκη απομακρυσμένων αποθετηρίων

Έχουμε ήδη αναφέρει και έχουμε επιδείξει πώς η εντολή `git clone` _έμμεσα_ προσθέτει το απομακρυσμένο αποθετήριο `origin` στο αποθετήριό σας.
Ας δείτε πώς μπορείτε να προσθέσουμε ένα νέο απομακρυσμένο αποθετήριο _άμεσα_.(((εντολές git, remote)))
Για να προσθέσετε ένα νέο απομακρυσμένο αποθτεήριο Git με ένα σύντομο όνομα, το οποίο μπορείτε να θυμάστε εύκολα, εκτελέστε την εντολή `git remote add <shortname> <url>`:

[source,console]
Τώρα πλέον μπορείτε να χρησιμοποιείτε στη γραμμή εντολών τη συμβολοσειρά `pb` αντί για ολόκληρη τη διεύθυνση του αποθετηρίου.
Για παράδειγμα, αν θέλετε να ανακτήσετε (fetch) όλες τις πληροφορίες που έχει ο Paul στο αποθετήριό του, μπορείτε να εκτελέσετε την εντολή `git fetch pb`:

[source,console]

$ git fetch pb remote: Counting objects: 43, done. remote: Compressing objects: 100% (36/36), done. remote: Total 43 (delta 10), reused 31 (delta 5) Unpacking objects: 100% (43/43), done. From https://github.com/paulboone/ticgit * [new branch] master → pb/master * [new branch] ticgit → pb/ticgit

Πλέον μπορείτε να χρησιμοποιείτε τη συμβολοσειρά `pb` στη γραμμή εντολών αντί για ολόκληρο το URL.
Για παράδειγμα, αν θέλετε να ανακτήσετε (fetch) όλες τις πληροφορίες που έχει ο Paul αλλά δεν τις έχετε στο αποθετήριό σας, μπορείτε να εκτελέσετε  `git fetch pb`:

[source,console]

$ git fetch pb remote: Counting objects: 43, done. remote: Compressing objects: 100% (36/36), done. remote: Total 43 (delta 10), reused 31 (delta 5) Unpacking objects: 100% (43/43), done. From https://github.com/paulboone/ticgit * [new branch] master → pb/master * [new branch] ticgit → pb/ticgit

Ο κλάδος `master` του Paul είναι πλέον προσβάσιμος τοπικά σε σας ως `pb/master`.
Μπορείτε να τον συγχωνεύσετε σε κάποιον δικό σας κλάδο, ή να ανασύρετε (check out) έναν τοπικό κλάδο σε αυτό το σημείο, αν θέλετε να το επιθεωρήσετε.
Θα δείτε περισσότερα για τους κλάδους και πώς τους χρησιμοποιούμε στην ενότητα <<ch03-git-branching>>.

[[r_fetching_and_pulling]]
==== Ανάκτηση δεδομένων από απομακρυσμένα αποθετήρια

Όπως μόλις είδατε, για να πάρετε δεδομένα από απομακρυσμένα έργα, μπορείτε να εκτελέσετε:(((εντολές git, fetch)))

[source,console]

$ git fetch [remote-name]

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

Όταν κλωνοποιείτε ένα αποθετήριο, το αποθετήριο αυτό αποθηκεύεται με το όνομα `origin`.
Συνεπώς, η εντολή `git fetch origin` ανακτά όλες τις νέες αλλαγές που έχουν γίνει από τότε που κλωνοποιήσατε το αποθετήριο ή από τότε που ανακτήσατε δεδομένα από αυτό για τελευταία φορά.
Είναι σημαντικό να τονίσετε ότι η εντολή `git fetch` απλά τραβά δεδομένα στο τοπικό σας αποθετήριο· δεν συγχωνεύει τα δεδομένα αυτά με διάφορες αλλαγές που μπορεί να έχετε κάνει εσείς τοπικά.
Για να γίνει αυτό, θα πρέπει να κάνετε τη συγχώνευση χειροκίνητα όταν είστε έτοιμοι να το κάνετε.

Αν ο κλάδος που παρακολουθεί έναν απομακρυσμένα κλάδο (περισσότερες λεπτομέρειες για αυτό στην επόμενη ενότητα και την ενότητα <<ch03-git-branching>>), μπορείτε να χρησιμοποιήσετε την εντολή `git pull`.
Η εντολή αυτή θα ανακτήσει έναν απομακρυσμένο κλάδο και θα τον συγχωνεύσει αυτόματα στον τρέχοντα κλάδο σας.(((εντολές git, pull)))
Αυτή η ροή εργασιών ενδεχομένως σας φαίνεται πιο εύκολη· επιπλέον, η εντολή `git clone` θέτει αυτόματα τον τοπικό σας κλάδο `master` να παρακολουθεί τον απομακρυσμένο κλάδου `master` (ή όπως ονομάζεται ο προεπιλεγμένος κλάδος).
Η εκτέλεση της εντολής `git pull`, γενικά ανακτά τα δεδομένα από τον διακομιστή, τον οποίο είχατε αρχικά κλωνοποιήσει και προσπαθεί να συγχωνεύσε αυτά τα δεδομάνα στον κώδικα πάνω στον οποίο εργάζεστε.

[NOTE]
====
Από την έκδοση 2.27 του Git και μετά, η εντολή `git pull` σας προειδοποιεί αν η μεταβλητή `pull.rebase` δεν έχει καθοριστέι.
Το Git θα συνεχίσει να σας δίνει προειδοποιήσεις μέχρι να καθορίσετε αυτή τη μεταβλητή.

Αν θέλετε την προεπιλεμγένη συμπεριφορά του Git (αν είναι δυνατό κάνε fast-forward, αλλιώς δημιούργησε μία υποβολή συγχώνευσης (merge commit)):
`git config --global pull.rebase "false"`

Αν θέλετε να κάνετε rebase όταν ελκύετε:
`git config --global pull.rebase "true"`
====

[[r_pushing_remotes]]
==== Ώθηση δεδομένων σε απομακρυσμένα αποθετήρια

Όταν έχετε φέρει κάποιο έργο σας σε σημείο που θέλετε να το κοινοποιήσετε, θα πρέπει να το ωθήσετε.
Η εντολή είναι απλή: `git push <όνομα-απομακρυσμένου> <όνομα-κλάδου>`.(((εντολές git, push)))
Για παράδειγμα, αν θέλετε να ωθήσετε τον τοπικό σας κλάδο `master` στον απομακρυσμένο διακομιστή `origin` (επαναλαμβάνουμε ότι η κλωνοποίηση παραμετροποιεί αυτόματα αυτά τα ονόματα), τότε μπορείτε να εκτελέσετε αυτή την εντολή έτσι ώστε να ωθήσετε τις υποβολές (commits) που έχετε κάνει στον διακομιστή:

[source,console]

$ git push origin master

Η εντολή αυτή θα εκτελεστεί επιτυχώς μόνο αν έχετε κλωνοποιήσει από έναν διακομιστή στον οποίο έχετε δικαίωμα εγγραφής και αν κανείς άλλος δεν έχει ωθήσει δεδομένα στο μεσοδιάστημα.
Αν εσείς και κάποιος άλλος έχετε κλωνοποιήσει το έργο ενώ αυτό βρίσκεται στην ίδια κατάσταση και αυτός ωθήσει δεδομένα στον διακομιστή και μετά ωθήσετε εσείς δεδομένα στον διακομιστή, η δική σας εντολή για ώθηση δεδομένων θα απορριφθεί.
Αυτό που θα πρέπει να κάνετε είναι να ανακτήσετε τις αλλαγές του άλλου και να τις ενσωματώσετε στις δικές σας, ώστε να σας επιτραπεί να ωθήσετε.
Στην ενότητα <<ch03-git-branching>> θα δείτε περισσότερες πληροφορίες σχετικά με την ώθηση δεδομένων σε απομακρυσμένους διακομιστές.

[[r_inspecting_remote]]
==== Επιθεώρηση απομακρυσμένου αποθετηρίου

Αν θέλετε να δείτε περισσότερες πληροφορίες σχετικά με ένα απομακρυσμένο αποθετήριο, μπορείτε να χρησιμοποιήσετε την εντολή `git remote show <όνομα-απομακρυσμένου>`.(((εντολές git, remote)))
Αν εκτελέσετε αυτή την εντολή αυτή με κάποιο σύντομο όνομα, όπως το `origin`, θα δείτε κάτι σαν:

[source,console]

$ git remote show origin * remote origin Fetch URL: https://github.com/schacon/ticgit Push URL: https://github.com/schacon/ticgit HEAD branch: master Remote branches: master tracked dev-branch tracked Local branch configured for git pull: master merges with remote master Local ref configured for git push: master pushes to master (up to date)

Παρατίθεται το URL του απομακρυσμένου αποθετηρίου καθώς και οι κλάδοι τους οποίους παρακολουθείτε.
Επίσης σας ενημερώνει ότι αν βρίσκεστε στον κλάδο `master` και εκτελέσετε `git pull`, ο κλάδος `master` του απομακρυσμένου αποθετηρίου θα συγχωνευτεί αυτόματα στον τοπικό κλάδο `master` αφότου ανακτηθεί.
Επίσης παραθέτει τις απομακρυσμένες αναφορές που έχει κατεβάσει.

Το παραπάνω είναι ένα απλό παράδειγμα που ενδεχομένως θα συναντήσετε.
Όταν αρχίσετε να χρησιμοποιείτε πιο εκτεταμένα το Git, μπορεί να δείτε πολύ περισσότερες πληροφορίες όταν εκτελείτε την εντολή `git remote show`.
Για παράδειγμα:

[source,console]

$ git remote show origin * remote origin URL: https://github.com/my-org/complex-project Fetch URL: https://github.com/my-org/complex-project Push URL: https://github.com/my-org/complex-project HEAD branch: master Remote branches: master tracked dev-branch tracked markdown-strip tracked issue-43 new (next fetch will store in remotes/origin) issue-45 new (next fetch will store in remotes/origin) refs/remotes/origin/issue-11 stale (use git remote prune to remove) Local branches configured for git pull: dev-branch merges with remote dev-branch master merges with remote master Local refs configured for git push: dev-branch pushes to dev-branch (up to date) markdown-strip pushes to markdown-strip (up to date) master pushes to master (up to date)

Αυτή η εντολή σας δείχνει σε αυτή την περίπτωση σε ποιον κλάδο ωθείτε δεδομένα όταν βρίσκεστε σε συγκεκριμένους κλάδους και εκτελείτε την εντολή `git push`.
Επίσης σας δείχνει ποιους απομακρυσμένους κλάδους του διακομιστή δεν έχετε ακόμα, ποιους απομακρυσμένους κλάδους έχετε αλλά έχουν αφαιρεθεί από τον διακομιστή, καθώς και τους κλάδους που θα συγχωνευτούν αυτόματα στους τοπικούς κλάδους που τους παρακολουθούν αν εκτελέσετε την εντολή `git pull`.

==== Μετονομασία και διαγραφή απομακρυσμένων αποθετηρίων

Αν θέλετε να μετονομάσετε το σύντομο όνομα ενός απομακρυσμένου αποθετηρίου, εκτελείτε την εντολή `git remote rename`.(((εντολές git, remote)))
Για παράδειγμα, αν θέλετε να μετονομάσετε το `pb` σε `paul`, μπορείτε να χρησιμοποιήσετε την `git remote rename`:

[source,console]

$ git remote rename pb paul $ git remote origin paul

Αξίζει να σημειωθεί ότι η εντολή αυτή αλλάζει επίσης τα ονόματα των απομακρυσμένων κλάδων που παρακολουθείτε.
Στον κλάδο στον οποίο αναφερόσασταν ως `pb/master` πλέον θα αναφέρεστε ως `paul/master`.

Μπορεί επίσης για κάποιο λόγο να θέλετε να διαγράψετε ένα απομακρυσμένο αποθετήριο.
Για παράδειγμα, μπορεί ο διακομιστής έχει μετακινηθεί σε άλλη διεύθυνση ή δεν χρησιμοποιείτε καθόλου το συγκεκριμένο αποθετήριο, ή απλά κάποιος συνεργάτης έχει εγκαταλείψει.
Σε αυτή την περίπτωση μπορείτε να χρησιμοποιήσετε την εντολή `git remote rm`:

[source,console]

$ git remote rm paul $ git remote origin

[[r_git_tagging]]
=== Ετικέτες

(((ετικέτες)))(((tags)))
Το Git, όπως και τα περισσότερα VCS, δίνει τη δυνατότητα να βάζουμε ετικέτες (tags) σε συγκεκριμένα σημεία του ιστορικού ενός έργου.
Η λειτουργικότητα αυτή χρησιμοποείται συνήθως για σημειωθούν συγκεκριμένες εκδόσεις (π.χ. έκδοση `1.0`, `2.0` κ.ο.κ.).
Σε αυτή την ενότητα θα μάθετε πώς να βλέπετε ποιες ετικέτες έχει ένα έργο, πώς να δημιουργείτε ετικέτες και ποια είδη ετικετών υπάρχουν.

==== Παράθεση ετικετών

Για να δείτε τις ετικέτες ενός έργου, η εντολή στο Git είναι αρκετά απλή.
Απλά πληκτρολογήστε `git tag` (και προαιρετικά `-l` ή `--list`):(((εντολές git, tag)))

[source,console]

$ git tag v0.1 v1.3

Η εντολή παραθέτει τις ετικέτες σε αλφαβητική σειρά, αν και η σειρά αυτή δεν έχει κάποια ιδιαίτερη σημασία.

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

[source,console]

$ git tag -l v1.8.5* v1.8.5 v1.8.5-rc0 v1.8.5-rc1 v1.8.5-rc2 v1.8.5-rc3 v1.8.5.1 v1.8.5.2 v1.8.5.3 v1.8.5.4 v1.8.5.5

[NOTE]
.Παράθεση ετικετών με χαρακτήρες υποκατάστασης απαιτεί την επιλογή `-l` ή `--list`
====
Αν θέλετε μόνο την πλήρη λίστα των ετικετών, τότε αν εκτελέσετε την εντολή `git tag` υποθέτει ότι θέλετε μια λίστα και την εμφανίζει· η χρήση του `-l` ή του `--list` σε αυτή την περίπτωση είναι προαιρετική.

Αν όμως δίνετε και ένα μοτίβο με χαρακτήρες υποκατάσταση (wildcards), τότε η χρήση ενός από τα `-l` ή `--list` είναι υποχρεωτική.
====

==== Δημιουργία ετικετών

Το Git χρησιμοποιεί δύο κατηγορίες ετικετών, τις απλές (lightweight) και τις ετικέτες με επισημείωση (annotated).

Μια απλή ετικέτα μοιάζει πολύ με έναν κλάδο που δεν αλλάζει· είναι απλά ένας δείκτης σε μια συγκεκριμένη υποβολή.

Οι ετικέτες με επισημειώσεις από την άλλη, αποθηκεύονται στη βάση δεδομένων του Git ως πλήρη αντικείμενα.
Για καθεμία επισημειωμένη ετικέτα: υπολογίζεται το άθροισμα ελέγχου (checksum) της· περιέχει το όνομα, και τη διεύθυνση e-mail αυτού που βάζει την ετικέτα και την ημερομηνία· έχει ένα μήνυμα· μπορεί να υπογραφεί και να επαληθευθεί με του GNU Privacy Guard (GPG).
Γενικά συνιστάται να δημιουργείτε ετικέτες με επισημειώσεις (annotations) έτσι ώστε να έχετε όλες αυτές τις πληροφορίες.
Παρόλα αυτά, αν για κάποιο λόγο θέλετε μία προσωρινή ετικέτα ή να μια ετικέτα χωρίς περαιτέρω πληροφορίες, μπορείτε να χρησιμοποιήσετε τις απλές ετικέτες.

[[r_annotated_tags]]
==== Επισημασμένες ετικέτες

(((ετικέτες, επισημασμένες)))(((tags, annotated)))
Η δημιουργία επισημασμένων ετικετών είναι απλή.
Ο ευκολότερος τρόπος είναι να χρησιμοποιήσετε την επιλογή `-a` όταν εκτελείτε την εντολή `tag`:(((εντολές git, tag)))

[source,console]

$ git tag -a v1.4 -m my version 1.4 $ git tag v0.1 v1.3 v1.4

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

Χρησιμοποιώντας την εντολή `git show` μπορείτε να δείτε τις πληροφορίες που περιέχει μια ετικέτα:

[source,console]

$ git show v1.4 tag v1.4 Tagger: Ben Straub <ben@straub.cc> Date: Sat May 3 20:19:12 2014 -0700

my version 1.4

commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700

changed the version number
Η εντολή αυτή σας δείχνει πληροφορίες για τον χρήστη που έφτιαξε την ετικέτα, την ημερομηνία που τοποθετήθηκε η ετικέτα στην υποβολή (commit) και το μήνυμά της.

==== Απλές ετικέτες

(((ετικέτες, απλές)))(((tags, lightweight)))
Ένας άλλος τρόπος για να βάζετε ετικέτες στις υποβολές είναι οι απλές (lightweight) ετικέτες.
Μία τέτοια ετικέτα δεν είναι τίποτα άλλο από το άθροισμα ελέγχου της υποβολής σας, που αποθηκεύεται σε ένα αρχείο· δεν διατηρείται καμία άλλη πληροφορία.
Για να δημιουργήσετε μια απλή ετικέτα, θα πρέπει να μην χρησιμοποιήσετε τις επιλογές `-a`, `-s` ή `-m`:

[source,console]

$ git tag v1.4-lw $ git tag v0.1 v1.3 v1.4 v1.4-lw v1.5

Αν τώρα εκτελέσετε `git show` για τη συγκεκριμένη ετικέτα, δεν θα δείτε τις επιπλέον πληροφορίες που έχουν οι επισημασμένες ετικέτες.(((εντολές git, show)))
Θα δείτε μόνο την υποβολή:

[source,console]

$ git show v1.4-lw commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700

changed the version number
==== Προσάρτηση ετικέτας εκ των υστέρων

Μπορείτε επίσης να προσαρτήσετε ετικέτες σε παλαιότερες υποβολές.
Ας υποθέσουμε ότι το ιστορικό υποβολών σας είναι κάπως έτσι:

[source,console]

$ git log --pretty=oneline 15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch experiment a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support 0d52aaab4479697da7686c15f77a3d64d9165190 one more thing 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch experiment 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function 4682c3261057305bdd616e23b64b0857d832627b added a todo file 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo 8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme

Ας υποθέσουμε τώρα ότι ξεχάσατε να βάλετε ετικέτα στο έργο σας στην έκδοση v1.2 που ήταν η υποβολή με το μήνυμα `updated rakefile`.
Μπορείτε να προσαρτήσετε την ετικέτα αργότερα.
Για να το κάνετε αυτό, θα πρέπει να προσδιορίσετε το άθροισμα ελέγχου της υποβολής σας (ή ένα μέρος του) στο τέλος της εντολής:

[source,console]

$ git tag -a v1.2 9fceb02

Μπορείτε να δείτε ότι έχετε προσθέσει την ετικέτα στην υποβολή:(((εντολές git, tag)))

[source,console]

$ git tag v0.1 v1.2 v1.3 v1.4 v1.4-lw v1.5

$ git show v1.2 tag v1.2 Tagger: Scott Chacon <schacon@gee-mail.com> Date: Mon Feb 9 15:32:16 2009 -0800

version 1.2 commit 9fceb02d0ae598e95dc970b74767f19372d61af8 Author: Magnus Chacon <mchacon@gee-mail.com> Date: Sun Apr 27 20:43:35 2008 -0700

    updated rakefile
...
[[r_sharing_tags]]
==== Κοινοποίηση ετικετών

Εξ ορισμού, η εντολή `git push` δεν μεταφέρει ετικέτες στους απομακρυσμένους διακομιστές.(((εντολές git, push)))
Θα πρέπει να ορίσετε ρητά ότι θέλετε να ωθήσετε τις ετικέτες στον διακομιστή, αφού προηγουμένως τις έχετε δημιουργήσει.
Η διαδικασία είναι παρόμοια με την κοινοποίηση απομακρυσμένων κλάδων· εκτελείτε την εντολή `git push origin <tagname>`.

[source,console]

$ git push origin v1.5 Counting objects: 14, done. Delta compression using up to 8 threads. Compressing objects: 100% (12/12), done. Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done. Total 14 (delta 3), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new tag] v1.5 → v1.5

Αν έχετε πολλές ετικέτες που θέλετε να ωθήσετε με τη μία, μπορείτε επίσης να χρησιμοποιήσετε την επιλογή `--tags` στην εντολή `git push`.
Με τον τρόπο αυτό θα μεταφέρετε στον διακομιστή όλες τις ετικέτες που δεν είναι ήδη εκεί.

[source,console]

$ git push origin --tags Counting objects: 1, done. Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done. Total 1 (delta 0), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new tag] v1.4 → v1.4 * [new tag] v1.4-lw → v1.4-lw

Πλέον, όταν κάποιος κλωνοποιήσει ή ελκύσει δεδομένα από το αποθετήριό σας, θα λάβει μαζί και όλες τις ετικέτες σας.

[NOTE]
.Η εντολή `git push` ωθεί και τα δύο είδη ετικετών
====
Η εντολή `git push <remote> --tags` θα ωθήσει τόσο τις απλές όσο και τις επισημασμένες ετικέτες.
Προς το παρόν δεν υπάρχει κάποιος τρόπος να ωθήσετε μόνο τις απλές ετικέτες, αλλά αν εκτελέσετε `git push <remote> --follow-tags` μόνο οι επισημασμένες ετικέτες θα ωθηθούν στον απομακρυσμένο διακομιστή.
====

==== Διαγραφή ετικετών

Για να διαγράψετε μια ετικέτα στο τοπικό σας αποθετήριο, μπορείτε να εκτελέσετε `git tag -d <tagname>`.
Για παράδειγμα, μπορείτε να διαγράψουμε την απλή ετικέτα που δημιουργήσαμε παραπάνω ως εξής:

[source,console]

$ git tag -d v1.4-lw Deleted tag v1.4-lw (was e7d5add)

Σημειώστε ότι η εντολή αυτή δεν διαγράφει την ετικέτα από τους απομακρυσμένους διακομιστές.
Υπάρχουν δύο παραλλαγές για τη διαγραφή ετικετών από έναν απομακρυσμένο διακομιστή.

Η πρώτη είναι η εντολή `git push <remote> :refs/tags/<tagname>`:

[source,console]

$ git push origin :refs/tags/v1.4-lw To /git@github.com:schacon/simplegit.git - [deleted] v1.4-lw

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

Η δεύτερη και πιο διαισθητική) είναι να διαγράψετε την ετικέτα ως εξής:

[source,console]

$ git push origin --delete <tagname>

==== Ανάσυρση (check out) ετικετών

Για να δείτε τις εκδόσεις των αρχείων σας στα οποία δείχνει μία ετικέτα, μπορείτε να εκτελέσετε  `git checkout` για αυτή την ετικέτα, αλλά αυτό θέτει το αποθετήριό σας σε κατάσταση "`detached HEAD`", κάτι που έχει κάποιες παρενέργειες:

[source,console]

$ git checkout v2.0.0 Note: switching to v2.0.0.

You are in detached HEAD state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example:

git switch -c <new-branch-name>

Or undo this operation with:

git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 99ada87…​ Merge pull request #89 from schacon/appendix-final

$ git checkout v2.0-beta-0.1 Previous HEAD position was 99ada87…​ Merge pull request #89 from schacon/appendix-final HEAD is now at df3f601…​ Add atlas.json and cover image

Στην κατάσταση "`detached HEAD`", αν κάνετε αλλαγές και δημιουργήσετε μια υποβολή (commit), η ετικέτα θα παραμείνει η ίδια, αλλά η νέα υποβολή δεν θα ανήκει σε κανέναν κλάδο και δεν θα είναι προσβάσιμη με κανέναν τρόπο εκτός κι αν χρησιμοποιήσετε το ακριβές hash της υποβολής.
Με άλλα λόγια, αν χρειάζετα να κάνετε αλλαγές -- ας πείτε ότι διορθώνετε κάποιο σφάλμα κάποιας παλιότερης έκδοσης, για παράδειγμα -- καλό θα είναι να δημιουργήσετε έναν κλάδο:

[source,console]

$ git checkout -b version2 v2.0.0 Switched to a new branch version2

Αν βέβαια εκτελέσετε την παραπάνω εντολή και πραγματοποιήσετε μια υποβολή, ο κλάδος `version2` θα είναι λίγο διαφορετικός από την ετικέτα `v2.0.0` διότι θα έχει προχωρήσει με τις νέες αλλαγές· συνεπώς προσοχή.




[[r_git_aliases]]
=== Συντομεύεσεις στο Git

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

Το Git δεν μπορεί να μαντέψει μια εντολή αν τη γράψετε μόνο μερικώς.
Αν δεν θέλετε να πληκτρολογείτε όλα τα γράμματα των εντολών του Git, μπορείτε εύκολα να ορίσετε ένα ψευδώνυμο για κάθε εντολή με την εντολή `git config`.(((εντολές git, config)))
Μερικά παραδείγματα για το πώς μπορείτε να ορίσετε μερικά ψευδώνυμα:

[source,console]

$ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.ci commit $ git config --global alias.st status

Αυτό σημαίνει ότι μπορείτε, για παράδειγμα, να πληκτρολογήσετε `git ci` αντί για `git commit`.
Καθώς χρησιμοποιείτε το Git, θα δείτε ότι υπάρχουν και άλλες εντολές που χρησιμοποιείτε συχνά -- μη διστάσετε να δημιουργήσετε ψευδώνυμα για αυτές τις εντολές.

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

[source,console]

$ git config --global alias.unstage reset HEAD --

Αυτό καθιστά τις δύο παρακάτω εντολές ισοδύναμες:

[source,console]

$ git unstage fileA $ git reset HEAD fileA

Η εντολή που εκτελείτε φαίνεται πλέον πιο καθαρά.
Το ψευδώνυμο `last` είναι επίσης πολύ συνηθισμένο:

[source,console]

$ git config --global alias.last log -1 HEAD

Με αυτό τον τρόπο μπορείτε να δείτε πιο εύκολα την τελευταία υποβολή:

[source,console]

$ git last commit 66938dae3329c7aebe598c2246a8e6af90d04646 Author: Josh Goebel <dreamer3@example.com> Date: Tue Aug 26 19:48:51 2008 +0800

test for current head
Signed-off-by: Scott Chacon <schacon@example.com>
Όπως μπορείτε να δείτε, το Git μπορεί να αντικαταστήσει μια εντολή με οποιοδήποτε ψευδώνυμο ορίσετε.
Μπορεί όμως αντί για μια εντολή του Git, να θέλετε να εκτελέσετε μια εξωτερική εντολή.
Στην περίπτωση αυτή, θα πρέπει να ξεκινήσετε την εντολή με τον χαρακτήρα `!`.
Αυτό θα σας φανεί χρήσιμο αν γράφετε δικά σας εργαλεία που δουλεύουν με αποθετήρια Git.
Για παράδειγμα, μπορείτε να χρησιμοποιήσετε το ψευδώνυμο `git visual` για να εκτελέσετε την εντολή `gitk`:

[source,console]

$ git config --global alias.visual !gitk

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

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


[#ch03-git-branching]
[[r_git_branching]]
== Διακλαδώσεις στο Git

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

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

[[r_git_branches_overview]]
=== Οι κλάδοι με λίγα λόγια

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

Όπως ίσως θυμάστε από την ενότητα <<ch01-introduction>>, το Git δεν αποθηκεύει τα δεδομένα ως μία ακολουθία αλλαγών ή διαφορών αλλά ως μία ακολουθία _στιγμιότυπων_ (snapshots).

Όταν κάνετε μία υποβολή (commit), το Git αποθηκεύει ένα αντικείμενο υποβολής που περιέχει έναν δείκτη προς το στιγμιότυπο του περιεχομένου που έχει υποβληθεί.
Αυτό το αντικείμενο περιέχει επίσης το όνομα και e-mail του συγγραφέα, το μήνυμα που έχετε πληκτρολογήσει καθώς και δείκτες προς την υποβολή ή τις υποβολές που προηγήθηκαν ακριβώς πριν από αυτή την υποβολή (δηλαδή, τον γονέα ή τους γονείς της): όταν ένα αρχείο υποβάλλεται για πρώτη φορά, τότε δεν έχει κανέναν γονέα· μία συνηθισμένη υποβολή έχει έναν γονέα, ενώ μία υποβολή που προέκυψε από τη συγχώνευση δύο ή περισσότερων κλάδων έχει περισσότερους από έναν γονέα.

Για να το οπτικοποιήσουμε λίγο, ας υποθέσουμε ότι έχετε έναν κατάλογο που περιέχει τρία αρχεία, τα οποία έχετε προσθέσει στον προθάλαμο και στη συνέχεια υποβάλετε (commit).
Κατά την προσθήκη των αρχείων στον προθάλαμο υπολογίζονται τα αθροίσματα ελέγχου (checksums) των αρχείων (με τον αλγόριθμο SHA-1, που αναφέρθηκε στην ενότητα <<ch01-introduction>>), αποθηκεύονται οι συγκεκριμένες εκδόσεις των αρχείων στο αποθετήριο Git (το Git ονομάζει αυτές τις εκδόσεις _blobs_) και προσθέτει τα αθροίσματα ελέγχου στην ενδιαμέση περιοχή.

[source,console]

$ git add README test.rb LICENSE $ git commit -m Initial commit

Όταν κάνετε την υποβολή, τρέχοντας την εντολή `git commit`, το Git υπολογίζει ένα άθροισμα ελέγχου για καθέναν υποκατάλογο (στη συγκεκριμένη περίπτωση μόνο για τον βασικό κατάλογο του έργου) και τους αποθηκεύει ως αντικείμενα δομής δένδρου στο αποθετήριο Git.
Στη συνέχεια το Git δημιουργεί ένα αντικείμενο υποβολής που περιέχει τα μεταδεδομένα και έναν δείκτη στη βάση του δένδρου του έργου, ώστε να μπορεί να επαναδημιουργήσει το στιγμιότυπο, όταν χρειαστεί.(((εντολές git, commit)))

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

.Μια υποβολή και το δένδρο της.
image::images/commit-and-tree.png[Μια υποβολή και το δένδρο της.]

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

.Υποβολές (commits) και οι γονείς τους.
image::images/commits-and-parents.png[Υποβολές (commits) και οι γονείς τους.]

Οι κλάδοι του Git είναι απλά μετακινήσιμοι δείκτες σε υποβολές.
Το προεπιλεγμένο όνομα κλάδου στο Git είναι `master` (κύριος κλάδος).
Όταν ξεκινάτε να κάνετε υποβολές, σας δίνεται ένας κλάδος `master` που δείχνει στην τελευταία υποβολή που κάνατε.
Κάθε φορά που υποβάλλετε, ο κλάδος `master` προχωρά αυτόματα.

[NOTE]
====
Ο κλάδος `master` στο Git δεν είναι κάποιος ειδικός κλάδος.(((master)))
Είναι ακριβώς το ίδιο με οποιονδήποτε άλλο κλάδο.
Ο μόνος λόγος για τον οποίο σχεδόν κάθε αποθετήριο έχει έναν κλάδο `master` είναι επειδή η εντολή `git init` τον ονομάζει έτσι και οι περισσότεροι χρήστες του Git δεν ασχολούνται με το να τον αλλάξουν.
====

.Ένας κλάδος και το ιστορικό υποβολών του.
image::images/branch-and-history.png[Ένας κλάδος και το ιστορικό υποβολών του.]


[[r_create_new_branch]]
==== Δημιουργία νέου κλάδου

(((κλάδοι, δημιουργία)))
Τι γίνεται όταν δημιουργείτε έναν νέο κλάδο;
Αυτό που συμβαίνει είναι ότι δημιουργείται ένα νέος δείκτης τον οποίο μπορείτε να μετακινείτε από δω κι από κει.
Ας πείτε ότι δημιουργείτε έναν νέο κλάδο που ονομάζεται `testing`.
Αυτό γίνεται με την εντολή `git branch`:(((εντολές git, branch)))

[source,console]

$ git branch testing

Αυτή η εντολή δημιουργεί έναν νέο δείκτη στην υποβολή στην οποία βρίσκεστε αυτή τη στιγμή.

.Δύο κλάδοι που δείχνουν στην ίδια ακολουθία υποβολών.
image::images/two-branches.png[Δύο κλάδοι που δείχνουν στην ίδια ακολουθία υποβολών.]

Πώς όμως γνωρίζει το Git σε ποιον κλάδο βρίσκεστε τώρα;
Το γνωρίζει επειδή διατηρεί έναν ειδικό δείκτη που ονομάζεται `HEAD`.
Σημειώστε ότι αυτός ο δείκτης `HEAD` είναι πολύ διαφορετικός από την έννοια του `HEAD` με την οποία είστε ενδεχομένως εξοικειωμένοι σε άλλα VCS, όπως το Subversion ή το CVS.
Στο Git αυτός είναι ένας δείκτης στον τοπικό κλάδο στον οποίο στον οποίο βρίσκεστε αυτή τη στιγμή.
Στη συγκεκριμένη περίπτωση, βρίσκεστε ακόμα στον κλάδο `master`.
Η εντολή `git branch` απλά _δημιούργησε_ έναν νέο κλάδο -- δεν μετέβη σε αυτό τον κλάδο.

.Ο δείκτης HEAD δείχνει σε έναν κλάδο.
image::images/head-to-master.png[Ο δείκτης HEAD δείχνει σε έναν κλάδο.]

Αυτό μπορείτε να το διαπιστώσετε εύκολα τρέχοντας την εντολή `git log` με την επιλογή `--decorate`, που παραθέτει πού δείχνουν οι δείκτες των κλάδων.

[source,console]

$ git log --oneline --decorate f30ab (HEAD → master, testing) Add feature #32 - ability to add new formats to the central interface 34ac2 Fix bug #1328 - stack overflow under certain conditions 98ca9 Initial commit

Βλέπετε τους κλάδους `master` και `testing` που βρίσκονται δίπλα στην υποβολή `f30ab`.


[[r_switching_branches]]
==== Μετάβαση σε άλλο κλάδο

(((κλάδοι, μετάβαση)))
Για να μεταβείτε σε έναν ήδη υπάρχοντα κλάδο, τρέχετε την εντολή `git checkout`.(((εντολές git, checkout)))
Ας μεταβείτε στον νέο κλάδο `testing`:

[source,console]

$ git checkout testing

Η εντολή αυτή μετατοπίζει τον δείκτη `HEAD` ώστε να δείχνει στον κλάδο `testing`.

.Ο `HEAD` δείχνει στον τρέχοντα κλάδο.
image::images/head-to-testing.png[Ο `HEAD` δείχνει στον τρέχοντα κλάδο.]

Ποια είναι η σημασία αυτού του πράγματος;
Ας κάνουμε ακόμα μία υποβολή:

[source,console]

$ vim test.rb $ git commit -a -m Make a change

.Ο κλάδος `HEAD` μετακινείται ότι γίνεται μία υποβολή.
image::images/advance-testing.png[Ο κλάδος `HEAD` μετακινείται ότι γίνεται μία υποβολή.]

Αυτό είναι ενδιαφέρον, διότι τώρα ο νέος σας κλάδος `testing` έχει προχωρήσει, αλλά ο κλάδος `master` ακόμα δείχνει στην υποβολή που βρισκόσασταν όταν είχατε τρέξει την εντολή `git checkout` για να αλλάξετε κλάδο.
Ας επιστρέψουμε στον κλάδο `master`:

[source,console]

$ git checkout master

[NOTE]
.Η εντολή `git log` δεν δείχνει _όλους_ τους κλάδους _πάντα_
====
Αν εκτελούσατε `git log` τώρα, θα αναρωτιόσασταν πού πήγε ο κλάδος `testing` που μόλις δημιουργήσατε, αφού δεν θα εμφανιζόταν στην έξοδο της εντολής.

Ο κλάδος δεν έχει εξαφανιστεί· το Git δεν γνωρίζει ότι ενδιαφέρεστε για αυτόν τον κλάδοκαι προσπαθεί να σας δείξει μόνο αυτό για το οποίο νομίζει ότι σας ενδιαφέρει.
Με άλλα λόγια, εξ ορισμού, η `git log` θα σας δείξει το ιστορικό υποβολών του κλάδου τον οποίο έχετε κάνει checked out.

Για να δείτε όλο το ιστορικό υποβολών για τον κλάδο που θέλετε, πρέπει να το πείτε ρητά: `git log testing`.
Για να δείτε το ιστορικό όλων των κλάδων, προσθέστε και το `--all` στην εντολή `git log`.
====

.Ο δείκτης `HEAD` μετακινείται όταν εκτελείτε `checkout`.
image::images/checkout-master.png[Ο δείκτης `HEAD` μετακινείται όταν εκτελείτε `checkout`.]

Αυτή η εντολή έκανε δύο πράγματα:
Μετατόπισε τον δείκτη `HEAD` ώστε να ξαναδείχνει στον κλάδο `master` και επανέφερε τα αρχεία στον τρέχοντα κατάλογο στο στιγμιότυπο που δείχνει ο κλάδος `master'.
Αυτό σημαίνει επίσης ότι όποιες αλλαγές κάνετε από αυτό το σημείο και μετά θα αποκλίνουν από μια  παλιότερη έκδοση του έργου.
Ουσιαστικά αναιρεί όποιες αλλαγές έχετε κάνει στον κλάδο `testing` ώστε να μπορέσετε να κινηθείτε σε μία διαφορετική κατεύθυνση.

[NOTE]
.Η μετάβαση από έναν κλάδο σε άλλον αλλάζει τα αρχεία στον κατάλογο εργασίας
===
Είναι σημαντικό να τονιστεί ότι όταν μετακινείστε από έναν κλάδο σε άλλο στο Git, τα αρχεία στον τρέχοντα κατάλογο αλλάζουν.
Αν μεταβείτε σε κάποιον παλιότερο κλάδο, ο τρέχων κατάλογος θα επαναφερθεί στην κατάσταση στην οποία βρισκόταν την τελευταία φορά που είχατε κάνει κάποια υποβολή σε αυτό τον κλάδο.
Αν το Git δεν μπορεί να το κάνει χωρίς προβλήματα, δεν θα σας επιτρέψει να μεταβείτε σε αυτό τον κλάδο.
===

Ας κάνουμε μερικές ακόμα αλλαγές και να τις υποβάλλουμε:

[source,console]

$ vim test.rb $ git commit -a -m Make other changes

Τώρα το ιστορικό του έργου έχει αποκλίνει (βλ. <<rdivergent_history>>).
Δημιουργήσατε έναν κλάδο, μεταβήκατε σε αυτόν, κάνατε κάποιες αλλαγές και μετά επιστρέψατε στον κύριο κλάδο σας και κάνατε κάποιες άλλες αλλαγές.
Οι αλλαγές αυτές είναι απομονωμένες σε διαφορετικούς κλάδους: μπορείτε να μεταπηδείτε από τον έναν κλάδο στον άλλο και να τους συγχωνεύσετε όταν είστε έτοιμοι.
Και όλα αυτά τα καταφέρνετε με απλές εντολές `branch`, `checkout` και `commit`.

[[rdivergent_history]]
.Αποκλίνον ιστορικό.
image::images/advance-master.png[Αποκλίνον ιστορικό.]

Αυτό μπορείτε επίσης να το δείτε εύκολα με την εντολή `git log`.
Αν εκτελέσετε την εντολή `git log --oneline --decorate --graph --all` θα εκτυπωθεί το ιστορικό των υποβολών στο οποίο θα φαίνεται πού βρίσκονται οι δείκτες των κλάδων σας και με ποιον τρόπο έχει αποκλίνει το ιστορικό.

[source,console]

$ git log --oneline --decorate --graph --all * c2b9e (HEAD, master) Make other changes | * 87ab2 (testing) Make a change |/ * f30ab Add feature #32 - ability to add new formats to the central interface * 34ac2 Fix bug #1328 - stack overflow under certain conditions * 98ca9 Initial commit of my project

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

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

Ας δείτε γιατί πρέπει να το κάνετε αυτό.

[NOTE]
.Δημιουργία κλάδου και μετάβαση σε αυτόν με τη μία.
====
Είναι σύνηθες όταν δημιουργείτε έναν κλάδο να θέλουμε να μεταβείτε σε αυτόν άμεσα -- αυτό μπορεί να γίνει με την εκτέλεση μίας μόνο εντολής, της `git checkout -b <όνομα-νέου-κλάδου>`.
====

[NOTE]
====
Από την έκδοση 2.23 του Git και μετά, μπορείτε να χρησιμοποιείτε την `git switch` αντί για `git checkout` ώστε:

- Να μεταβείτε σε έναν προϋπάρχοντα κλάδο: `git switch testing-branch`.
- Να δημιουργήσετε έναν νέο κλάδο και να μεταβείτε σε αυτόν: `git switch -c new-branch`.
  Η σημαία `-c` σημαίνει create, μπορείτε επίσης να χρησιμοποιείτε την πλήρη σημαία: `--create`.
- Να επιστρέψετε στον αμέσως προηγούμενο κλάδο που είχατε κάνει checkout: `git switch -`.
====


=== Βασικές έννοιες διακλαδώσεων και συγχωνεύσεων

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

. Θα κάνετε αλλαγές σε μία ιστοσελίδα.
. Θα δημιουργήσετε έναν κλάδο για μία νέα ιστορία χρήστη (user story) την οποία δουλεύετε.
. θα κάνετε αλλαγές σε αυτό τον κλάδο.

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

. Θα μεταβείτε στον κλάδο παραγωγής.
. Θα δημιουργήσετε έναν κλάδο στον οποίο θα προσθέσετε την επείγουσα λυση (hotfix).
. Αφού ο κώδικάς σας δοκιμαστεί, θα συγχωνεύσετε τον κλάδο με το hotfix και θα τον ωθήσετε στην παραγωγή.
. Θα επιστρέψετε στην ιστορία χρήστη και θα συνεχίσετε να την δουλεύετε.

[[r_basic_branching]]
==== Διακλαδώσεις -- τα βασικά

(((κλάδοι, βασική ροή εργασίας)))
Αρχικά ας υποθέσουμε ότι δουλεύετε σε ένα έργο και έχετε κάνει ήδη μερικές υποβολές στον κλάδο `master`.

.Ένα απλό ιστορικό υποβολών.
image::images/basic-branching-1.png[Ένα απλό ιστορικό υποβολών.]

Αποφασίσατε ότι θα δουλέψετε στο πρόβλημα #53 του συστήματος παρακολούθησης προβλημάτων που χρησιμοποιεί η εταιρία σας.
Για να δημιουργήσετε έναν κλάδο και να μεταβείτε σε αυτό συγχρόνως, μπορείτε να τρέξετε την εντολή `git checkout` με τη σημαία `-b`:

[source,console]

$ git checkout -b iss53 Switched to a new branch "iss53"

Η παραπάνω εντολή είναι συντομογραφία για το εξής:

[source,console]

$ git branch iss53 $ git checkout iss53

.Δημιουργία νέου δείκτη σε κλάδο.
image::images/basic-branching-2.png[Δημιουργία νέου δείκτη σε κλάδο.]

Επεξεργάζεστε την ιστοσελίδα σας και κάνετε μερικές υποβολές.
Με τις υποβολές ο κλάδος `iss53` προχωρά, διότι τον έχετε κάνει checkout (δηλαδή ο `HEAD` δείχνει σε αυτό τον κλάδο):
[source,console]

$ vim index.html $ git commit -a -m Create new footer [issue 53]

.Ο κλάδος `iss53` προχώρησε εξαιτίας των αλλαγών σας.
image::images/basic-branching-3.png[Ο κλάδος `iss53` προχώρησε εξαιτίας των αλλαγών σας.]

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

Ωστόσο πριν το κάνετε αυτό, σημειώστε ότι αν υπάρχουν στον κατάλογο εργασίας σας ή στον προθάλαμο αλλάγες που δεν έχουν υποβληθεί και έρχονται σε σύγκρουση με τον κλάδο στον οποίο θέλετε να μεταβείτε, το Git δεν θα σας αφήσει να αλλάξετε κλάδο.
Το καλύτερο είναι να έχετε μία καθαρή κατασταση εργασίας όταν μεταβαίνετε από έναν κλάδο σε άλλο.
Υπάρχουν τρόποι να παρακάμψετε αυτή τη συμπεριφορά (με τις εντολές `git stash` και `git commit --amend` που θα καλύψουμε στη συνέχεια, στην ενότητα <<r_git_stashing>>.
Προς το παρόν, ας υποθέσουμε ότι έχετε υποβάλλει όλες τις αλλαγές σας, ώστε να μπορέσετε να επιστρέψετε στον κλάδο `master`:

[source,console]

$ git checkout master Switched to branch master

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

Στη συνέχεια, πρέπει να δουλέψετε για το hotfix.
Ας φτιάξουμε έναν κλάδο `hotfix` στον οποίο θα εργαστείτε:

[source,console]

$ git checkout -b hotfix Switched to a new branch hotfix $ vim index.html $ git commit -a -m Fix broken email address [hotfix 1fb7853] Fix broken email address 1 file changed, 2 insertions(+)

.Κλάδος `hotfix` που βασίζεται στον κλάδο `master`.
image::images/basic-branching-4.png[Κλάδος `hotfix` που βασίζεται στον κλάδο `master`.]

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

[source,console]

$ git checkout master $ git merge hotfix Updating f42c576..3a0874c Fast-forward index.html | 2 + 1 file changed, 2 insertions()

Σε αυτή τη συγχώνευση υπάρχει η έκφραση `fast-forward`.
Επειδή η υποβολή `C4` στον οποίο έδειχνε ο κλάδος `hotfix` τον οποίο συγχωνεύσατε ήταν ακριβώς μπροστά από την υποβολή `C2` στην οποία είστε, το Git απλά μετακίνησε τον δείκτη προς τα εμπρός.
Με άλλα λόγια όταν προσπαθείτε να συγχωνεύσετε μία υποβολή με μία άλλη υποβολή στην οποία μπορείτε να φτάσετε ακολουθώντας το ιστορικό της πρώτης, το Git απλοποιεί τη διαδικασία μετακινώντας τον δείκτη σε εκείνο το σημείο, διότι δεν υπάρχει άλλη αποκλίνουσα εργασία που θα πρέπει να συγχωνευτεί -- αυτό ονομάζεται "`ταχυπροώθηση`" ("`fast-forward`").

Η αλλαγή σας τώρα υπάρχει στο στιγμιότυπο της υποβολής στην οποία δείχνει ο κλάδος `master` και μπορείτε να δημοσιεύσετε τη διόρθωσή σας.

.Ο κλάδος `master` ταχυπροωθήθηκε στον κλάδο `hotfix`.
image::images/basic-branching-5.png[Ο κλάδος `master` ταχυπροωθήθηκε στον κλάδο `hotfix`.]

Αφού ο σημαντικότατος διορθωτικός σας κώδικας έχει δημοσιευτεί, είστε έτοιμοι να επανέλθετε στην εργασία την οποία κάνατε πριν σας διακόψει το τηλεφώνημα.
Προτού όμως συνεχίσετε, θα διαγράψετε τον κλάδο `hotfix`, διότι δεν τον χρειάζεστε πλέον -- ο κλάδος `master` δείχνει ακριβώς στην ίδια θέση.
Μπορείτε να τον διαγράψετε με την επιλογή `-d` στην εντολή `git branch`:

[source,console]

$ git branch -d hotfix Deleted branch hotfix (3a0874c).

Τώρα μπορείτε να επιστρέψετε στον κλάδο εργασίας του προβλήματος #53 και να συνεχίσετε να δουλεύετε σ' αυτό.

[source,console]

$ git checkout iss53 Switched to branch "iss53" $ vim index.html $ git commit -a -m Finish the new footer [issue 53]

1 file changed, 1 insertion(+)

.Η εργασία συνεχίζει στον κλάδο `iss53`.
image::images/basic-branching-6.png[Η εργασία συνεχίζει στον κλάδο `iss53`.]

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

[[r_basic_merging]]
==== Συγχωνεύσεις -- τα βασικά

(((κλάδοι, συγχώνευση)))(((συγχώνευση)))(((branches, merging)))(((merging)))
Ας υποθέσουμε τώρα ότι έχετε αποφασίσει ότι η εργασία σας για το πρόβλημα #53 έχει ολοκληρωθεί και είναι έτοιμη να συγχωνευτεί στον κλάδο `master`.
Για να το κάνετε αυτό, αρκεί να συγχωνεύσετε τον κλάδο `iss53` στον κλάδο `master`, λίγο-πολύ με τον ίδιο τρόπο που συγχωνεύσατε τον κλάδο `hotfix` προηγουμένως.
Το μόνο που έχετε να κάνετε είναι να μεταβείτε στον κλάδο στον οποίο θέλετε να ενσωματώσετε τον άλλο κλάδο και να τρέξετε την εντολή `git merge`:

[source,console]

$ git checkout master Switched to branch master $ git merge iss53 Merge made by the recursive strategy. index.html | 1
1 file changed, 1 insertion(+)

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

.Τρία στιγμιότυπα που χρησιμοποιούνται σε μία τυπική συγχώνευση.
image::images/basic-merging-1.png[Τρία στιγμιότυπα που χρησιμοποιούνται σε μία τυπική συγχώνευση.]

Αντί, λοιπόν, το Git να μετακινήσει τον δείκτη του κλάδου προς τα εμπρός, δημιουργεί ένα νέο στιγμιότυπο που προκύπτει από αυτή την τριμερή συγχώνευση και αυτομάτως δημιουργεί μία νέα υποβολή που δείχνει σε αυτό το στιγμιότυπο.
Αυτό ονομάζεται _υποβολή συγχώνευσης_ (merge commit) και έχει την ιδιαιτερότητα ότι έχει περισσότερους από έναν γονείς.

.Μία υποβολή συγχώνευσης.
image::images/basic-merging-2.png[Μία υποβολή συγχώνευσης.]

Τώρα που η εργασία σας έχει συγχωνευτεί, δεν χρειάζεστε πλέον τον κλάδο `iss53`.
Μπορείτε να κλείσετε το ζήτημα στο σύστημα παρακολούθησης προβλημάτων σας και να διαγράψετε τον κλάδο:

[source,console]

$ git branch -d iss53

[[r_basic_merge_conflicts]]
==== Συγκρούσεις συγχωνεύσεων -- τα βασικά

(((συγχώνευση, συγκρούσεις)))(((merging, conflicts)))
Ενίοτε, η διαδικασία συγχώνευσης δεν εξελίσσεται τόσο ομαλά.
Αν έχετε τροποποιήσει το ίδιο σημείο του ίδιου αρχείου με διαφορετικό τρόπο στους δύο κλάδους που συγχωνεύετε, το Git δεν θα μπορέσει να τους συγχωνεύσει παστρικά.
Αν η λύση σας για το πρόβλημα #53 και η λύση σας για το επείγον πρόβλημα τροποποίησαν το ίδιο τμήμα ενός αρχείου, θα πάρετε ένα μήνυμα _σύγκρουσης συγχώνευσης_ περίπου σαν το παρακάτω:

[source,console]

$ git merge iss53 Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.

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

[source,console]

$ git status On branch master You have unmerged paths. (fix conflicts and run "git commit")

Unmerged paths: (use "git add <file>…​" to mark resolution)

both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

Οτιδήποτε εμπλέκεται σε σύγκρουση συγχώνευσης και δεν έχει επιλυθεί καταγράφεται ως unmerged.
Το Git προσθέτει τυποποιημένους σημειωτές "`επίλυσης σύγκρουσης`" στα αρχεία που εμπλέκονται σε συγκρούσεις, ώστε να τα ανοίξετε και να επιλύσετε αυτές τις διαφορές.
Το αρχείο σας θα περιέχει ένα τμήμα που θα φαίνεται κάπως έτσι:

[source,html]

<<<<<<< HEAD:index.html <div id="footer">contact : email.support@github.com</div>

<div id="footer"> please contact us at support@github.com </div> >>>>>>> iss53:index.html

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

[source,html]

<div id="footer"> please contact us at email.support@github.com </div>

Αυτή η επίλυση της σύγκρουσης περιέχει λίγο από κάθε τμήμα και οι γραμμές που περιέχουν τα `<<<<<<<`, `=======` και `>>>>>>>` έχουν αφαιρεθεί εντελώς.
Αφού έχετε επιλύσει όλα τα τμήματα σε κάθε αρχείο που εμπλέκεται σε σύγκρουση, τρέχετε `git add` σε καθένα από αυτά τα αρχεία, ώστε να επισημανθεί ως επιλυμένο.
Αν το αρχείο περάσει στον προθάλαμο, αυτό σημαίνει ότι έχει επιλυθεί.

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

[source,console]

$ git mergetool

This message is displayed because merge.tool is not configured. See git mergetool --tool-help or git help config for more details. git mergetool will now attempt to use one of the following tools: opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge Merging: index.html

Normal merge conflict for index.html: {local}: modified file {remote}: modified file Hit return to start merge resolution tool (opendiff):

Αν θέλετε να χρησιμοποιήσετε κάποιο εργαλείο συγχώνευσης διαφορετικό από το προεπιλεγμένο (το Git επέλεξε το `opendiff` σε αυτή την περίπτωση, διότι εκτελέσαμε την εντολή σε Mac), μπορείτε να δείτε όλα τα εργαλεία που υποστηρίζονται στο πάνω μέρος μετά από το `one of the following tools`:
Απλά γράψτε το όνομα του εργαλείου που προτιμάτε.

[NOTE]
====
Αν χρειάζεστε πιο προχωρημένα εργαλεία για να επιλύετε περίπλοκες συγκρούσεις συγχωνεύσεων, θα μιλήσουμε σχετικά στην ενότητα <<r_advanced_merging>>.
====

Αφού βγείτε από το εργαλείο συγχώνευσης, το Git σάς ρωτάει αν η συγχώνευση ήταν επιτυχής.
Αν του πείτε ότι ήταν, ωθεί το αρχείο στον προθάλαμο ώστε να επισημανθεί ως επιλυμένο.
Μπορείτε να τρέξετε την εντολή `git status` ξανά για να επιβεβαιώσετε ότι όλες οι συγκρούσεις έχουν επιλυθεί:

[source,console]

$ git status On branch master All conflicts fixed but you are still merging. (use "git commit" to conclude merge)

Changes to be committed:

modified:   index.html
Αν είστε ευχαριστημένοι με το αποτέλεσμα και επιβεβαιώσετε ότι όλα τα αρχεία που εμπλέκονται σε συγκρούσεις έχουν τοποθετηθεί στον προθάλαμο, μπορείτε να πληκτρολογήσετε `git commit` για να οριστικοποιήσετε την υποβολή συγχώνευσης.
Το μήνυμα υποβολής είναι εξ ορισμού κάπως έτσι:

[source,console]

Merge branch iss53

Conflicts: index.html # # It looks like you may be committing a merge. # If this is not correct, please remove the file # .git/MERGE_HEAD # and try again.

# Please enter the commit message for your changes. Lines starting # with # will be ignored, and an empty message aborts the commit. # On branch master # All conflicts fixed but you are still merging. # # Changes to be committed: # modified: index.html #

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


[[r_branch_management]]
=== Διαχείριση κλάδων

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

Η εντολή `git branch` εκτός από το να δημιουργεί και να διαγράφει κλάδους κάνει και κάποια άλλα πράγματα.(((εντολές git, branch)))
Αν την τρέξετε χωρίς ορίσματα, τότε παίρνετε μία λίστα όλων των κλάδων:

[source,console]

$ git branch iss53 * master testing

Ο χαρακτήρας `*` πριν από τον κλάδο `master` επισημαίνει ότι ο κλάδος αυτός είναι ο τρέχων κλάδος (δηλαδή ο κλάδος στον οποίο δείχνει ο δείκτης `HEAD`).
Αυτό σημαίνει ότι αν κάνετε μία υποβολή σε αυτό το σημείο, ο κλάδος `master` θα προχωρήσει ώστε να συμπεριλάβει τη δουλειά σας.
Για να δείτε την τελευταία υποβολή του κάθε κλάδου μπορείτε να τρέξετε την εντολή `git branch -v`:

[source,console]

$ git branch -v iss53 93b412c Fix javascript issue * master 7a98805 Merge branch iss53 testing 782fd34 Add scott to the author list in the readme

Οι επιλογές `--merged` και `--no-merged` φιλτράρουν τη λίστα των κλάδων και δείχνουν μόνον όσους κλάδους έχουν συγχωνευτεί και αντίστοιχα δεν έχουν ακόμα συγχωνευτεί στον τρέχοντα κλάδο.
Για να δείτε ποιοι κλάδοι έχουν ήδη συγχωνευτεί στον τρέχοντα κλάδο, τρέχετε την εντολή `git branch --merged`:

[source,console]

$ git branch --merged iss53 * master

Επειδή είχατε ήδη συγχωνεύσει τον κλάδο `iss53` προηγουμένως, φαίνεται στη λίστα σας.
Γενικά είναι ασφαλές να διαγράψετε τους κλάδους αυτής της λίστας που δεν έχουν το `*` χρησιμοποιώντας την εντολή `git branch -d`· έχετε ήδη ενσωματώσει τις αλλαγές τους σε κάποιον άλλο κλάδο, συνεπώς δεν πρόκειται να χάσετε τίποτα.

Για να δείτε όλους τους κλάδους που περιέχουν εργασία που δεν έχετε συγχωνεύσει σε κάποιον άλλο κλάδο ακόμα, μπορείτε να εκτελέσετε την εντολή `git branch --no-merged`:

[source,console]

$ git branch --no-merged testing

Αυτή δείχνει τον άλλο σας κλάδο.
Επειδή αυτός ο κλάδος περιέχει εργασία που δεν έχει ακόμα συγχωνευτεί σε κάποιον άλλο κλάδο, αν αποπειραθείτε να τον διαγράψετε με την εντολή `git branch -d` θα αποτύχετε:

[source,console]

$ git branch -d testing error: The branch testing is not fully merged. If you are sure you want to delete it, run git branch -D testing.

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

[TIP]
====
Εφόσον δεν δώσετε το όνομα μίας υποβολής ή ενός κλάδου οι επιλογές `--merged` και `--no-merged` θα σας δείξουν τι έχει ή δεν έχει συγχωνευτεί, αντίστοιχα, στον _τρέχοντα_ κλάδο σας.

Μπορείτε επίσης να δώσετε μία επιπρόσθετη παράμετρο για να ρωτήσετε την κατάσταση συγχώνευσης σε σχέση με κάποιον άλλο κλάδο χωρίς να έχετε μεταβεί σε αυτό τον κλάδο προηγουμένως.
Για παράδειγμα, στο παρακάτω ρωτάμε, "`τι δεν έχει συγχωνευτεί στον κλάδο `master` ακόμα;`"
[source,console]

$ git checkout testing $ git branch --no-merged master topicA featureB

====

==== Μετονομασία κλάδου

[CAUTION]
====
Μην μετονομάζετε κλάδους που χρησιμοποιούνται ακόμα από άλλους συνεργάτες.
Μην μετονομάζετε κλάδους όπως τους master/main/mainline χωρίς να έχετε διαβάσει την ενότητα <<_changing_master>>.
====

Ας υποθέσουμε ότι έχετε έναν κλάδο με το όνομα `bad-branch-name` και θέλετε να το αλλάξετε σε `corrected-branch-name`, διατηρώνταςα όλο το ιστορικό.
Επίσης θέλετε να αλλάξετε το όνομα του κλάδου στον απομακρυσμένο (GitHub, GitLab, ή άλλο)  διακομιστή.
Πώς μπορείτε να το κάνετε;

Μετονομάστε τον κλάδο τοπικά με την εντολή `git branch --move`:

[source, console]

$ git branch --move bad-branch-name corrected-branch-name

Η εντολή αυτή αντικαθιστά το `bad-branch-name` με το `corrected-branch-name`, αλλά αυτή η αλλαγή προς το παρόν έχει γίνει μόνο τοπικά.
Για να δουν και οι άλλοι τον διορθωμένο κλάδο, πρέπει να τον ωθήσετε:

[source,console]

$ git push --set-upstream origin corrected-branch-name

Ας δείτε τώρα που βρισκόμαστε:

[source, console]

$ git branch --all * corrected-branch-name main remotes/origin/bad-branch-name remotes/origin/corrected-branch-name remotes/origin/main

Παρατηρήστε ότι βρίσκεστε στον κλάδο `corrected-branch-name` και ότι επιπλέον είναι διαθέσιμος και στον απομακρυσμένο διακομιστή.
Όμως και ο κλάδος με το προηγούμενο όνομα υπάρχει ακόμα στον απομακρυσμένο διακομιστή.
Μπορείτε να τον διαγράψετε εκτελώντας την ακόλουθη εντολη:

[source,console]

$ git push origin --delete bad-branch-name

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

[[_changing_master]]
===== Αλλαγή του ονόματος του κύριου κλάδου

[WARNING]
====
Η μετονομασία των κλάδων όπως οι master/main/mainline/default θα διακόψει την αφομοίωση, τις υπηρεσίες, τα βοηθητικά προγράμματα και τα script μεγαλώττισης που χρησιμοποιεί το αποθετήριό σας. .
Πριν το κάνετε, οπωσδήποτ μιλήστε με τους συνεργάτες σας.
Επίσης, κάντε μια ενδελεχή αναζήτηση στο αποθετήριό σας και ενημερώστε αναφορές στο παλιό όνομα του κύριου κλάδου σας στον κώδικα και τα script σας.
====

Μπορείτε να μετονομάσετε τον τοπικό σας κλάδο `master` σε `main` με την εξής εντολή:

[source,console]

$ git branch --move master main

Πλέον δεν υπάρχει τοπικός κλάδος `master`, διότι έχει μετονομαστεί σε `main`.

Για να δουν και οι άλλοι τον κλάδο `main`, πρέπει να τον ωθήσετε στον απομακρυσμένο διακομιστή.
Αυτό γίνεται με την εξής εντολή.

[source,console]

$ git push --set-upstream origin main

Έχετε καταλήξει στην ακόλουθη κατάσταση:

[source,console]

$ git branch --all * main remotes/origin/HEAD → origin/master remotes/origin/main remotes/origin/master

Ο τοπικός σας κλάδος `master` έχει εξαφανιστεί, αφού αντικαταστάθηκε από τον κλάδο `main`.
Ο κλάδος `main` υπάρχει στον απομακρυσμένο διακομιστή.
Όμως και ο κλάδος `master` υπάρχει ακόμα στον απομακρυσμένο διακομιστή.
Κάποιοι συνεργάτες σας θα συνεχίσουν να χρησιμοποιύν τον κλάδο `master` ως τον κύριο κλάδο τους, μέχρι να κάνετε μερικές ακόμα αλλαγές.

Πρέπει να κάνετε μερικά ακόμα πράγματα ώστε να ολοκληρώσετε τη μετάβαση:

* Όσα έργα εξαρτώνται από αυτό, θα πρέπει να ενημερώσουν τον κώδικά τους ή/και την παραμετροποίησή τους.
* Ενημερώστε όποια αρχεία παραμετροποίησης test-runner έχετε.
* Προσαρμόστε τα script build και release.
* Ενημερώστε ρυθμίσεις στον διακομιστή του αποθετηρίου σας όσον αφορά στον προεπιλεγμένο κλάδο, τους κανόνες συγχώνευσης και ό,τι άλλο χρησιμοποιεί ονόματα κλάδων.
* Ενημερώστε αναφορές στον παλιό όνομα στην τεκμηρίωση.
* Κλείστε ή συγχωνεύστε όσα αιτήματα ελκυσμού έχουν ως στόχο τον παλιό κλάδο.

Αφού έχετε κάνει όλα αυτά και είστε σίγουροι ότι ο κλάδος `main` αποδίδει όπως απέδιδε και ο κλάδος `master`, μπορείτε να διαγράψετε τον κλάδο `master`:

[source, console]

$ git push origin --delete master

=== Ροές εργασίας με διακλαδώσεις

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

==== Μακρόβιοι κλάδοι

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

Πολλοί προγραμματιστές που χρησιμοποιούν το Git έχουν μία ροή εργασιών που υιοθετεί αυτή την προσέγγιση.
Για παράδειγμα στον κλάδο `master` έχουν αποκλειστικά κώδικα που είναι απολύτως σταθερός (stable) -- ενδεχομένως μόνον κώδικα που έχει δημοσιευτεί ή θα δημοσιευτεί.
Έχουν έναν άλλο παράλληλο κλάδο που τον ονομάζουν `develop` ή `next` στον οποίο δουλεύουν ή στον οποίο ελέγχουν την σταθερότητα -- αυτός δεν είναι απαραίτητα σταθερός, αλλά όποτε καθίσταται σταθερός ενδεχομένως συγχωνεύεται με τον κλάδο `master`.
Ο κλάδος αυτός χρησιμοποιείται για να ενσωματώνει _θεματικούς κλάδους_ (βραχύβιους κλάδους, όπως ο κλάδος `iss53` που είδατε προηγουμένως) όταν αυτοί είναι έτοιμοι, ώστε να είναι σίγουροι ότι περνούν όλα τα τεστ και δεν εισάγουν σφάλματα (bugs).

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

.Γραμμική προβολή διακλάδωσης με προοδευτική ευστάθεια.
image::images/lr-branches-1.png[Γραμμική προβολή διακλάδωσης με προοδευτική ευστάθεια.]

Γενικά είναι ευκολότερο να τους σκέφτεστε σαν σιλό (silo) εργασίας, από τα οποία κάθε τόσο κάποιες υποβολές, που έχουν δοκιμαστεί πλήρως, προκρίνονται σε άλλα, σταθερότερα σιλό.

[[rlrbranch_b]]
.Προβολή σιλό διακλάδωσης με προοδευτική ευστάθεια.
image::images/lr-branches-2.png[Προβολή σιλό διακλάδωσης με προοδευτική ευστάθεια.]

Μπορείτε να συνεχίζετε να κάνετε κάτι τέτοιο για πολλά επίπεδα ευστάθειας.
Κάποια μεγαλύτερα έργα έχουν επίσης έναν κλάδο `proposed` ή `pu` (proposed updates) που έχει ενσωματωμένους κλάδους, οι οποίοι ενδεχομένως δεν είναι έτοιμοι να εισαχθούν στον κλάδο `next` ή τον κλάδο `master`.
Η λογική είναι ότι οι κλάδοι βρίσκονται σε διαφορετικά επίπεδα ευστάθειας· όταν φτάσουν σε ένα πιο σταθερό επίπεδο, συγχωνεύονται με τον κλάδο που βίσκεται από πάνω τους.
Επαναλαμβάνουμε ότι το να υπάρχουν πολλούς μακρόβιοι κλάδοι δεν είναι απαραίτητο, αλλά συχνά είναι χρήσιμο, ειδικά όταν έχετε πολύ μεγάλα ή περίπλοκα έργα.

[[r_topic_branch]]
==== Θεματικοί κλάδοι

(((κλάδοι, θεματικοί)))(((κλάδοι, topic)))
Από την άλλη, οι θεματικοί κλάδοι είναι χρήσιμοι σε έργα οποιουδήποτε μεγέθους.
Ένας θεματικός κλάδος είναι ένας βραχύβιος κλάδος, τον οποίο δημιουργείτε και χρησιμοποιείτε αποκλειστικά για μία συγκεκριμένη λειτουργικότητα (feature) ή κάποια σχετική με αυτή εργασία.
Αυτό είναι κάτι που πιθανότατα δεν έχετε κάνει ποτέ σε ένα VCS στο παρελθόν, διότι γενικά η διαδικασία δημιουργίας και συγχώνευσης κλάδων είναι μία _ακριβή_ διαδικασία.
Όμως στο Git είναι σύνηθες κάποιος να δημιουργεί κλάδους, να εργάζεται με αυτούς, να τους συγχωνεύει και να τους διαγράφει πολλές φορές κάθε μέρα.

Αυτό το είδατε στην προηγούμενη ενότητα με τους κλάδους `iss53` και `hotfix` που δημιουργήσατε.
Κάνατε μερικές υποβολές και τους διαγράψατε αμέσως αφού τους συγχωνεύσατε με τον κύριο κλάδο.
Αυτή η τεχνική σας επιτρέπει να αλλάζετε περιβάλλον γρήγορα και πλήρως -- επειδή η εργασία σας είναι διαχωρισμένη σε κλάδους-σιλό στους οποίους όλες οι αλλαγές έχουν να κάνουν με αυτό θέμα, είναι ευκολότερο να δείτε τι ακριβώς έχει γίνει κατά την επανεξέταση του κώδικα (code review) ή κάποια άλλη σχετική διαδικασία.
Μπορείτε να διατηρήσετε τις αλλαγές εκεί για λεπτά, μέρες ή μήνες, να τις συγχωνεύσετε όταν είναι έτοιμες για συγχώνευση ανεξάρτητα από τη χρονική σειρά με την οποία έχουν γίνει ή έχουν δουλευτεί.

Για παράδειγμα ας υποθέσουμε ότι έχετε κάνει κάποια δουλειά (στον κλάδο `master`), δημιουργήσατε έναν κλάδο για ένα πρόβλημα (`iss91`), δουλέψατε σε αυτό για λίγο, δημιουργήσατε έναν άλλο κλάδο για να δοκιμάσετε έναν άλλο τρόπο να επιλύσετε το ίδιο πρόβλημα (`iss91v2`), επιστρέψατε στον κλάδο `master` και δουλέψατε εκεί για λίγο και από κει φτιάξατε έναν άλλο κλάδο για να δουλέψετε πάνω σε κάποια ιδέα για την οποία δεν είστε σίγουροι ότι είναι καλή ιδέα (κλάδος `dumbidea`).
Το ιστορικό υποβολών θα είναι περίπου σαν το παρακάτω:

.Πολλαπλοί θεματικοί κλάδοι.
image::images/topic-branches-1.png[Πολλαπλοί θεματικοί κλάδοι.]

Ας υποθέσουμε τώρα ότι αποφασίσατε ότι σας άρεσε η δεύτερη λύση, δηλαδή η `iss91v2`, στο πρόβλημα· επιπλέον δείξατε τον κλάδο `dumbidea` στους συναδέλφους σας και αποδείχθηκε ότι είναι ιδιοφυής.
Μπορείτε να πετάξετε τον αρχικό κλάδο `iss91` (χάνοντας τις υποβολές `C5` και `C6`) και να συγχωνεύσετε αυτούς τους δύο.
Το ιστορικό τότε θα μοιάζει με το παρακάτω:

.Ιστορικό μετά τη συγχώνευση των κλάδων `dumbidea` και `iss91v2`.
image::images/topic-branches-2.png[Ιστορικό μετά τη συγχώνευση των κλάδων `dumbidea` και `iss91v2`.]

θα δείτε με περισσότερες λεπτομέρειες διάφορες δυνατές ροές εργασίας για τα έργα σας στο Git στο κεφάλαιο <<ch05-distributed-git>>.
Γι' αυτό καλό θα είναι να έχετε διαβάσει αυτό το κεφάλαιο προτού αποφασίσετε ποιο μοντέλο διακλαδώσεων θα χρησιμοποιήσετε στο επόμενο έργο σας.

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


[[r_remote_branches]]
=== Απομακρυσμένοι κλάδοι

(((κλάδοι, απομακρυσμένοι)))(((αναφορές, απομακρυσμένες)))
Οι απομακρυσμένες αναφορές είναι αναφορές (δείκτες) που βρίσκονται στα απομακρυσμένα αποθετήριά σας και  συμπεριλαμβάνουν κλάδους, ετικέτες και ούτω καθεξής.
Μπορείτε να πάρετε μία πλήρη λίστα των απομακρυσμένων αναφορών με την εντολή `git ls-remote <απομακρυσμένο-αποθετήριο>` ή `git remote show <απομακρυσμένο-αποθετήριο>` για απομακρυσμένους κλάδους καθώς και άλλες πληροφορίες.
Παρόλα αυτά, ένας πιο συνηθισμένος τρόπος είναι να εκμεταλλευτείτε τους κλάδους απομακρυσμένης παρακολούθησης (remote-tracking branches).

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

Έχουν τη μορφή `<απομακρυσμένο-αποθετήριο>/<κλάδος>`.
Για παράδειγμα, αν θέλετε να δείτε σε ποια κατάσταση ήταν ο κλάδος `master` στο απομακρυσμένο αποθετήριό σας `origin` την τελευταία φορά που επικοινωνήσατε μαζί του, θα πρέπει να μεταβείτε στον κλάδο `origin/master`.
Αν δουλεύατε σε ένα θέμα με κάποιον συνεργάτη και αυτός είχε ωθήσει έναν κλάδο `iss53`, ενδεχομένως είχατε κι εσείς έναν δικό σας τοπικό κλάδο `iss53`, αλλά ο κλάδος στον διακομιστή θα αναπαρίστατο με τον κλάδο απομακρυσμένης παρακολούθησης `origin/iss53`.

Ίσως όλα αυτά φαίνονται συγκεχυμένα, οπότε ας ξεμπερδέψουμε με ένα παράδειγμα.
Ας υποθέσουμε ότι έχετε έναν διακομιστή Git στο δίκτυό σας στη διεύθυνση `git.ourcompany.com`.
Αν τον κλωνοποιήσετε, η εντολή `clone` του Git θα τον ονομάσει `origin`, θα τραβήξει όλα τα δεδομένα και θα δημιουργήσει έναν δείκτη που δείχνει εκεί όπου βρίσκεται ο κλάδος του `master` και θα τον ονομάσει τοπικά `origin/master`.
Το Git επίσης θα δημιουργήσει έναν τοπικό κλάδο `master` για εσάς, ώστε να έχετε έναν κλάδο στον οποίο μπορείτε να δουλέψετε. Αυτός ο κλάδος ξεκινά από το ίδιο σημείο από όπου ξεκινά και ο κλάδος `master` του αποθετηρίου `origin`.

[NOTE]
.Το αποθετήριο `origin` δεν είναι κάτι ιδιαίτερο
====
Ακριβώς όπως το όνομα κλάδου `master` δεν έχει κάποια ιδιαίτερη σημασία στο Git, το ίδιο συμβαίνει και με το όνομα κλάδου `origin`.
Ενώ `master` είναι το προεπιλεγμένο όνομα για τον αρχικό κλάδο όταν τρέχετε την εντολή `git init` (που είναι και ο μόνος λόγος για τον οποίο χρησιμοποιείται τόσο ευρέως), `origin` είναι το προεπιλεγμένο όνομα για ένα απομακρυσμένο αποθετήριο όταν τρέχετε `git clone`.
Αν τρέξετε `git clone -o booyah` τότε θα έχετε ως προεπιλεγμένο απομακρυσμένο κλάδο τον `booyah/master`.(((origin)))
====

.Τοπικά και απομακρυσμένα αποθετήρια μετά την κλωνοποίηση.
image::images/remote-branches-1.png[Τοπικά και απομακρυσμένα αποθετήρια μετά την κλωνοποίηση.]

Αν κάνετε λίγη δουλίτσα στον τοπικό σας κλάδο `master` και στο μεταξύ κάποιος άλλος ωθήσει στο `git.ourcompany.com` και ενημερώσει τον κλάδο `master`, τότε τα δύο ιστορικά θα προχωρήσουν διαφορετικά.
Επιπλέον, για όσο χρονικό διάστημε δεν είστε συνδεδεμένοι με τον διακομιστή `origin`, ο δείκτης σας `origin/master` δεν μετακινείται.

.Η τοπική και απομακρυσμένη δουλειά μπορεί να αποκλίνουν.
image::images/remote-branches-2.png[Η τοπική και απομακρυσμένη δουλειά μπορεί να αποκλίνουν.]

Για να συγχρονίσετε τη δουλειά σας με κάποιο απομακρυσμένο αποθετήριο, τρέχετε την εντολή `git fetch <απομακρυσμένο-αποθετήριο>` (στην περίπτωσή μας `git fetch <origin>`).
Αυτή η εντολή αναζητά ποιος διακομιστής είναι ο `origin` (στη συγκεκριμένη περίπτωση είναι ο `git.ourcompany.com`), ανακτά (fetch) από αυτόν ό,τι δεδομένα δεν έχετε ακόμα και ενημερώνει την τοπική βάση δεδομένων σας, μετακινώντας τον δείκτη `origin/master` στη νέα του πιο ενημερωμένη θέση.

.Η εντολή `git fetch` ενημερώνει τους κλάδους απομακρυσμένης παρακολούθησης.
image::images/remote-branches-3.png[Η εντολή `git fetch` ενημερώνει τους κλάδους απομακρυσμένης παρακολούθησης.]

Για να δείξουμε τι συμβαίνει όταν έχετε πολλούς απομακρυσμένους διακομιστές και με τι μοιάζουν οι απομακρυσμένοι κλάδοι των απομακρυσμένων έργων, ας υποθέσουμε ότι έχετε ένα άλλο εσωτερικό διακομιστή Git που χρησιμοποιείται μόνον για ανάπτυξη κώδικα από μία συγκεκριμένη ομάδα.
Αυτός ο διακομιστής βρίσκεται στη διεύθυνση `git.team1.ourcompany.com`.
Μπορείτε να τον προσθέσετε στο έργο στο οποίο δουλεύετε ως μία νέα απομακρυσμένη αναφορά εκτελώντας την εντολή `git remote add` όπως εξηγήσαμε στο κεφάλαιο <<ch02-git-basics>>.
Ονομάστε αυτό τον απομακρυσμένο διακομιστή `teamone`, που θα είναι και το σύντομο όνομα του παραπάνω URL.

.Προσθήκη ενός επιπλέον απομακρυσμένου διακομιστή.
image::images/remote-branches-4.png[Προσθήκη ενός επιπλέον απομακρυσμένου διακομιστή.]

Τώρα μπορείτε να τρέξετε την εντολή `git fetch teamone` για να ανακτήσετε οτιδήποτε ο απομακρυσμένος διακομιστής `teamone` έχει που δεν το έχετε ακόμα εσείς.
Επειδή ο διακομιστής έχει ένα υποσύνολο από τα δεδομένα που έχει ο διακομιστής `origin` αυτή τη στιγμή, το Git δεν ανακτά δεδομένα, αλλά τοποθετεί έναν κλάδο απομακρυσμένης παρακολούθησης με όνομα `teamone/master` να δείχνει στην υποβολή που έχει ο `teamone` στον δικό του κλάδο `master`.

.Κλάδος απομακρυσμένης παρακολούθησης για τον κλάδο `teamone/master`.
image::images/remote-branches-5.png[Κλάδος απομακρυσμένης παρακολούθησης για τον κλάδο `teamone/master`.]

[[r_pushing_branches]]
==== Ωθήσεις

(((ώθηση)))(((pushing)))
Όταν θέλετε να μοιραστείτε έναν κλάδο με τον υπόλοιπο κόσμο, πρέπει να τον ωθήσετε σε έναν απομακρυσμένο διακομιστή στον οποίο έχετε δικαίωμα εγγραφής (write access).
Οι τοπικοί σας κλάδοι δεν συγχρονίζονται αυτόματα με τους απομακρυσμένους διακομιστές στους οποίους έχετε δικαίωμα να αποθηκεύετε -- πρέπει να ωθήσετε χειροκίνητα τους κλάδους που θέλετε να κοινοποιήσετε σε άλλους.
Με αυτό τον τρόπο, μπορείτε να χρησιμοποιείτε ιδιωτικούς κλάδους για δουλειά που δεν θέλετε να μοιραστείτε με άλλους και να ωθείτε μόνον τους θεματικούς κλάδους στους οποίους θέλετε να συνεργαστείτε.

Αν έχετε έναν κλάδο με όνομα `serverfix` στον οποίο θέλετε να δουλέψετε με άλλους, μπορείτε να τον ωθήσετε με τον ίδιο τρόπο που ωθήσατε τον πρώτο σας κλάδο.
Τρέχετε την εντολή `git push <απομακρυσμένο-αποθετήριο> <κλάδος>`:(((εντολές git, push)))

[source,console]

$ git push origin serverfix Counting objects: 24, done. Delta compression using up to 8 threads. Compressing objects: 100% (15/15), done. Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done. Total 24 (delta 2), reused 0 (delta 0) To https://github.com/schacon/simplegit * [new branch] serverfix → serverfix

Στην πραγματικότητα αυτή η εντολή είναι μία συντόμευση.
Το Git αναπτύσσει αυτόματα το όνομα κλάδου `serverfix` σε `refs/heads/serverfix:refs/heads/serverfix`, που σημαίνει "`Πάρε τον τοπικό μου κλάδο `serverfix` και ώθησέ τον ώστε να ενημερωθεί ο αντίστοιχος κλάδος `serverfix` του απομακρυσμένου διακομιστή`".
Θα δείτε πιο λεπτομερώς το κομμάτι `refs/heads/` στην ενότητα <<ch10-git-internals>>· προς το παρόν μπορείτε να το αγνοήσετε.
Επίσης μπορείτε να τρέξετε την εντολή `git push origin serverfix:serverfix`, που κάνει ακριβώς το ίδιο πράγμα -- λέει, "`Πάρε το δικό μου `serverfix` και κάντον το `serverfix` του απομακρυσμένου διακομιστή`".
Χρησιμοποιείτε αυτή την εντολή για να ωθήσετε έναν τοπικό κλάδο σε έναν απομακρυσμένο που έχει διαφορετικό όνομα.
Αν δεν θέλατε να ονομάζεται `serverfix` στο απομακρυσμένο αποθετήριο, τότε μπορείτε να τρέξετε την εντολή `git push origin serverfix:awesomebranch` ώστε να ωθήσετε τον τοπικό σας κλάδο `serverfix` στον κλάδο με όνομα `awesomebranch` στο απομακρυσμένο αποθετήριο.

[NOTE]
.Δεν χρειάζεται να γράφετε τον κωδικό πρόσβασής σας κάθε φορά.
====
Αν θέλετε να ωθήσετε κάτι σε ένα URL με HTTPS, ο διακομιστής Git θα σας ζητήσει το όνομα χρήστη και τον κωδικό σας για ταυτοποίηση.
Η προεπιλεγμένη ρύθμιση είναι να σας ζητήσει αυτή την πληροφορία στο τερματικό, ώστε ο διακομιστής να αποφανθεί αν έχετε το δικαίωμα να ωθήσετε αλλαγές.

Αν δεν θέλετε να πλκτρολογείτε τον κωδικό πρόσβασής σας κάθε φορά που ωθείτε κάτι, μπορείτε να ορίσετε μία "`προσωρινή μνήμη διαπιστευτηρίων`" ("`credential cache`").
Ο πιο απλός τρόπος είναι να παραμένουν στη μνήμη για μερικά λεπτά, κάτι που μπορεί να οριστεί εύκολα με την εντολή `git config --global credential.helper cache`.

Περισσότερες πληροφορίες σχετικά με τις διάφορες επιλογές προσωρινής αποθήκευσης διαπιστευτηρίων βλ. <<r_credential_caching>>.
====

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

[source,console]

$ git fetch origin remote: Counting objects: 7, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 3 (delta 0) Unpacking objects: 100% (3/3), done. From https://github.com/schacon/simplegit * [new branch] serverfix → origin/serverfix

Είναι σημαντικό να σημειώσουμε πως όταν εκτελείτε μία εντολή `git fetch`, που φέρνει νέους κλάδους απομακρυσμένης παρακολούθησης στον υπολογιστή σας, δεν έχετε αυτόματα τοπικά επεξεργάσιμα αρχεία.
Με άλλα λόγια, σε αυτή την περίπτωση, δεν έχετε έναν νέο κλάδο `serverfix` -- έχετε μόνον έναν δείκτη στον `origin/serverfix` που δεν μπορείτε να τροποποιήσετε.

Για να συγχωνεύσετε αυτή τη δουλειά στον τρέχοντα κλάδο εργασίας σας, μπορείτε να τρέξετε την εντολή `git merge origin/serverfix`.
Αν θέλετε τον δικό σας κλάδο `serverfix` στον οποίο μπορείτε να εργαστείτε, μπορείτε να τον βασίσετε στον κλάδο απομακρυσμένης παρακολούθησης:

[source,console]

$ git checkout -b serverfix origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch serverfix

Η παραπάνω εντολή σας δίνει έναν τοπικό κλάδο στον οποίο μπορείτε να δουλέψετε και ο οποίος ξεκινά από εκείνο το σημείο στο οποίο βρίσκεται ο κλάδος `origin/serverfix`.

[[r_tracking_branches]]
==== Παρακολούθηση κλάδων

(((κλάδοι, παρακολούθηση)))(((κλάδοι, upstream)))
Όταν κάνετε checkout έναν τοπικό κλάδο από έναν κλάδο απομακρυσμένης παρακολούθησης, αυτόματα δημιουργείται ένας "`κλάδος παρακολούθησης`" (και ο κλάδος που παρακολουθείται ονομάζεται "`κλάδος upstream`").
Οι κλάδοι παρακολούθησης είναι τοπικοί κλάδοι που σχετίζονται άμεσα με κάποιο απομακρυσμένο κλάδο.
Αν είστε σε έναν κλάδο παρακολούθησης και πληκτρολογήσετε `git pull`, το Git αυτόματα γνωρίζει από ποιον διακομιστή να ανακτήσει και σε ποιον κλάδο να συγχωνεύσει.

Όταν κλωνοποιείτε ένα αποθετήριο, αυτό δημιουργεί αυτόματα έναν κλάδο `master` που παρακολουθεί τον κλάδο `origin/master`.
Όμως μπορείτε να ορίσετε και άλλους κλάδους παρακολούθησης, αν θέλετε -- κλάδους που παρακολουθούν κλάδους σε άλλα απομακρυσμένα αποθετήρια ή δεν παρακολουθούν τον κλάδο `master`.
Η πιο απλή περίπτωση είναι αυτή που μόλις είδατε, η εντολή `git checkout -b <κλάδος> <απομακρυσμένο-αποθετήριο>/<κλάδος>`.
Αυτή η περίπτωση είναι τόσο συνηθισμένη που το Git σάς παρέχει την επιλογή `--track`:

[source,console]

$ git checkout --track origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch serverfix

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

[source,console]

$ git checkout serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch serverfix

Για να ορίσετε έναν τοπικό κλάδο με διαφορετικό όνομα από αυτό του απομακρυσμένου κλάδου, μπορείτε εύκολα να χρησιμοποιήσετε την πρώτη εκδοχή με διαφορετικό όνομα τοπικού κλάδου:

[source,console]

$ git checkout -b sf origin/serverfix Branch sf set up to track remote branch serverfix from origin. Switched to a new branch sf

Τώρα ο τοπικός σας κλάδος `sf` θα τραβά αυτόματα από τον κλάδο `origin/serverfix`.

Αν έχετε ήδη έναν τοπικό κλάδο και θέλετε να τον συνδέσετε με έναν απομακρυσμένο κλάδο που μόλις τραβήξατε ή θέλετε να αλλάξετε τον κλάδο upstream που παρακολουθείτε, μπορείτε να χρησιμοποιήσετε την επιλογή `-u` ή `--set-upstream-to` με την εντολή `git branch` ώστε να τον συνδέσετε οποιαδήποτε στιγμή.

[source,console]

$ git branch -u origin/serverfix Branch serverfix set up to track remote branch serverfix from origin.

[NOTE]
.Συντόμευση upstream
====
Όταν έχετε ορίσει έναν κλάδο παρακολούθησης, μπορείτε να αναφέρεστε σε αυτόν ως `@{upstream}` ή με τη συντομογραφία `@{u}`.
Άρα αν είστε στον κλάδο `master` και αυτός παρακολουθεί τον `origin/master`, μπορείτε να πείτε κάτι σαν `git merge @{u}` αντί για `git merge origin/master`, αν θέλετε.(((@{u})))(((@{upstream})))
====

Αν θέλετε να δείτε ποιους κλάδους παρακολούθησης έχετε ορίσει, μπορείτε να χρησιμοποιήσετε την επιλογή `-vv` στην εντολή `git branch`.
Αυτή θα παραθέσει όλους τους τοπικούς κλάδους με περισσότερες πληροφορίες, όπως ποιον κλάδο παρακολουθεί κάθε κλάδος και αν ο τοπικός σας κλάδος προηγείται ή υστερεί σε σχέση με τον απομακρυσμένο κλάδο ή και τα δύο.

[source,console]

$ git branch -vv iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets master 1ae2a45 [origin/master] deploying index fix * serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it testing 5ea463a trying something new

Εδώ μπορείτε να δείτε ότι ο κλάδος μας `iss53` παρακολουθεί τον `origin/iss53` και προηγείται κατά δύο (ahead 2), δηλαδή έχετε κάνει δύο υποβολές τοπικά που δεν έχουν ωθηθεί στον διακομιστή.
Επίσης μπορείτε να δείτε ότι ο τοπικός σας κλάδος `master` παρακολουθεί τον `origin/master` και είναι ενημερωμένος.
Στη συνέχεια βλέπουμε ότι ο κλάδος σας `serverfix` παρακολουθεί τον κλάδο `server-fix-good` στον διακομιστή `teamone` και προηγείται κατά τρεις και υστερεί κατά μία, που σημαίνει ότι υπάρχει μία υποβολή στον διακομιστή που δεν την έχετε συγχωνεύσει ακόμη και τρεις τοπικές υποβολές που δεν έχετε ωθήσει ακόμη.
Τέλος, βλέπετε ότι ο κλάδος σας `testing` δεν παρακολουθεί κανέναν απομακρυσμένο κλάδο.

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

[source,console]

$ git fetch --all; git branch -vv

==== Ελκυσμοί

(((ελκυσμός)))(((pulling)))
Ενώ η εντολή `git fetch` ανακτά όλες τις αλλαγές που δεν έχετε ήδη από τον διακομιστή, δεν θα αλλάξει τον κατάλογο εργασίας σας καθόλου.
Απλά θα πάρει τα δεδομένα και θα σας αφήσει να τα συγχωνεύσετε μόνοι σας.
Πάντως, υπάρχει η εντολή `git pull` η οποία ουσιαστικά στις περισσότερες περιπτώσεις είναι μία εντολή `git fetch` που ακολουθείται από μία εντολή `git merge`.
Αν έχετε ορίσει κάποιον κλάδο παρακολούθησης όπως σας δείξαμε στην προηγούμενη ενότητα, είτε με ρητό ορισμό είτε επειδή τον έχετε δημιουργήσει με τις εντολές `clone` ή `checkout`, η `git pull` θα αναζητήσει τον διακομιστή και κλάδο τον οποίο παρακολουθεί ο τρέχων κλάδος σας, θα ανακτήσει από τον διακομιστή και θα προσπαθήσει να συγχωνεύσει σε αυτό τον απομακρυσμένο κλάδο.

Γενικά είναι καλύτερα να χρησιμοποιείτε τις εντολές `fetch` και `merge` ξεχωριστά διότι τα μαγικά που κάνει η `git pull` μπορεί να σας μπερδέψουν.

[[r_delete_branches]]
==== Διαγραφή απομακρυσμένων κλάδων

(((κλάδοι, διαγραφή απομακρυσμένων)))
Ας υποθέσουμε ότι τελειώσατε με τον απομακρυσμένο κλάδο -- π.χ. οι συνεργάτες σας και εσείς τελειώσατε με ένα χαρακτηριστικό και το έχετε συγχωνεύσει στον κλάδο `master` του απομακρυσμένου αποθετηρίου (ή τέλος πάντων σε οποιονδήποτε κλάδο υπάρχει η ευσταθής έκδοσή του έργου σας).
Μπορείτε να διαγράψετε τον απομακρυσμένο κλάδο χρησιμοποιώντας την επιλογή `--delete` στην εντολή `git push`.
Αν θέλετε να διαγράψετε τον κλάδο `serverfix` από τον διακομιστή, τρέχετε τα παρακάτω:

[source,console]

$ git push origin --delete serverfix To https://github.com/schacon/simplegit - [deleted] serverfix

Βασικά αυτό που κάνει αυτή η εντολή είναι να απομακρύνει τον δείκτη από τον διακομιστή.
Ο διακομιστής Git γενικά θα διατηρήσει τα δεδομένα εκεί για λίγο καιρό, μέχρι να τρέξει μία διαδικασία συλλογής σκουπιδιών (garbage collection) ώστε αν ο κλάδος διαγράφηκε κατά λάθος, να είναι εύκολο να αποκατασταθεί.


[[r_rebasing]]
=== Αλλαγή βάσης (rebasing)

(((επανατοποθέτηση)))(((αλλαγή βάσης)))(((rebasing)))
Στο Git, υπάρχουν δύο βασικοί τρόποι ενσωμάτωσης αλλαγών από έναν κλάδο σε έναν άλλο: με την εντολή `merge` και την εντολή `rebase` (αλλαγή βάσης).
Σε αυτή την ενότητα θα μάθετε τι είναι η αλλαγή βάσης, πώς την κάνετε, γιατί θεωρείται εκπληκτικό εργαλείο και σε ποιες περιπτώσεις δεν πρέπει να τη χρησιμοποιήσετε.

==== Η βασική μορφή αλλαγής βάσης

Εάν επιστρέψετε σε ένα παλαιότερο παράδειγμα από την ενότητα <<r_basic_merging>>, θα δείτε ότι η δουλειά σας είχε αποκλίνει και είχατε κάνει υποβολές σε δύο διαφορετικούς κλάδους.

.Απλό αποκλίνον ιστορικό.
image::images/basic-rebase-1.png[Απλό αποκλίνον ιστορικό.]

Ο ευκολότερος τρόπος ενσωμάτωσης των κλάδων, όπως έχουμε ήδη δει, είναι η εντολή `merge` (συγχώνευση).
Επιτελεί μια τριμερή συγχώνευση μεταξύ των δύο τελευταίων στιγμιότυπων διακλάδωσης (`C3` και`C4`) και του πιο πρόσφατου κοινού προγόνου τους (`C2`), δημιουργώντας ένα νέο στιγμιότυπο (και μία νέα υποβολή).

.Συγχώνευση και ενσωμάτωση αποκλίνοντος ιστορικού εργασίας.
image::images/basic-rebase-2.png[Συγχώνευση και ενσωμάτωση αποκλίνοντος ιστορικού εργασίας.]

Ωστόσο, υπάρχει κι ένας άλλος τρόπος: μπορείτε να πάρετε μόνον το επίθεμα (patch) με τις τροποποιήσεις που εισήχθησαν με την υποβολή `C4` και να το εφαρμόσετε ξανά στο στιγμιότυπο `C3`.
Στο Git, αυτό ονομάζεται _αλλαγή βάσης_ ή _επανατοποθέτηση_ (rebasing).
Με την εντολή `rebase` παίρνετε όλες τις αλλαγές που υποβλήθηκαν σε ένα κλάδο και να τις επαναλαμβάνετε σε έναν άλλο.(((εντολές git, rebase)))

Για αυτό το παράδειγμα, πρώτα θα κάνατε checkout τον κλάδο `experiment` και στη συνέχεια θα τον επανατοποθετούσατε στον κλάδο `master` (δηλαδή, θα αλλάζατε τη βάση του από την υποβολή `C2` στην υποβολή που δείχνει ο κλάδος `master`) ως εξής:

[source,console]

$ git checkout experiment $ git rebase master First, rewinding head to replay your work on top of it…​ Applying: added staged command

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

.Αλλαγή της βάσης των τροποποιήσεων της `C4` από τη `C2` στη `C3`.
image::images/basic-rebase-3.png[Αλλαγή της βάσης των τροποποιήσεων της `C4` από τη `C2` στη `C3`.]

Σε αυτό το σημείο, μπορείτε να επιστρέψετε στον κλάδο `master` και να κάνετε μία συγχώνευση με ταχυπροώθηση.

[source,console]

$ git checkout master $ git merge experiment

.Ταχυπροώθηση του κλάδου `master`.
image::images/basic-rebase-4.png[Ταχυπροώθηση του κλάδου `master`.]

Πλέον το στιγμιότυπο στο οποίο δείχνει η υποβολή `C4'` είναι ακριβώς ίδιο με αυτό στο οποίο δείχνει η `C5` στο παλιότερο παράδειγμα με τη συγχώνευση.
Το τελικό προϊόν της ενσωμάτωσης των αλλαγών είναι ακριβώς το ίδιο, αλλά η αλλαγή της βάσης κρατά το ιστορικό πιο καθαρό.
Αν εξετάσετε το log ενός επανατοποθετημένου (σε νέα βάση) κλάδου, φαίνεται σαν το ιστορικό να είναι γραμμικό, δηλαδή σαν όλη η εργασία να συνέβη ακολουθιακά παρά το ότι αρχικά γινόταν παράλληλα.

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

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

==== Μερικές ενδιαφέρουσες αλλαγές βάσης

Μπορείτε επίσης να επανατοποθετήσετε έναν κλάδο πάνω σε κάποιον κλάδο διαφορετικό από αυτόν στον οποίο βασιζόταν αρχικά.
Για παράδειγμα, ας πάρουμε ένα ιστορικό όπως αυτό στο <<rrbdiag_e>>.
Έχετε δημιουργήσει έναν θεματικό κλάδο (`server`) για να προσθέσετε κάποια λειτουργικότητα από την πλευρά του διακομιστή στο έργο σας και πραγματοποιήσατε μια υποβολή.
Στη συνέχεια, δημιουργήσατε μία ακόμα διακλάδωση για να κάνετε τις αλλαγές από την πλευρά του πελάτη (`client`) και κάνατε επίσης μερικές υποβολές.
Τέλος, επιστρέψατε στον κλάδο `server` και κάνατε μερικές ακόμη υποβολές.

[[rrbdiag_e]]
.Ιστορικό με έναν θεματικό κλάδο που βασίζεται σε έναν άλλο θεματικό κλάδο
image::images/interesting-rebase-1.png[Ιστορικό με έναν θεματικό κλάδο που βασίζεται σε έναν άλλο θεματικό κλάδο]

Ας υποθέσουμε ότι αποφασίζετε να συγχωνεύσετε τις αλλαγές από την πλευρά του πελάτη στην κεντρική γραμμή που θα δημοσιευτεί, αλλά θέλετε να αναβάλλετε τις αλλαγές από την πλευρά του διακομιστή μέχρι να εξεταστούν περαιτέρω.
Μπορείτε να πάρετε τις αλλαγές από τον κλάδο `client` που δεν βρίσκονται στον κλάδο `server` (`C8` και`C9`) και να τις αναπαράγετε στον κύριο κλάδο σας χρησιμοποιώντας την επιλογή `--onto` της `git rebase`:

[source,console]

$ git rebase --onto master server client

Αυτή η εντολή ουσιαστικά λέει, "`Πήγαινε στον κλάδο `client`, εντόπισε τα επιθέματα από τότε που απέκλινε από τον κλάδο `server` και εφάρμοσέ τα στον κλάδο `client` σαν ο κλάδος `client` να ήταν κλάδος που απέκλινε από τον κλάδο `master``".
Είναι λίγο περίπλοκο, αλλά το αποτέλεσμα είναι μια ομορφιά.

.Αλλαγή βάσης ενός θεματικού κλάδου που βασίζεται σε έναν άλλο θεματικό κλάδο.
image::images/interesting-rebase-2.png[Αλλαγή βάσης ενός θεματικού κλάδου που βασίζεται σε έναν άλλο θεματικό κλάδο.]

Τώρα μπορείτε να ταχυπροωθήσετε τον κλάδο `master` (βλ. <<rrbdiag_g>>):

[source,console]

$ git checkout master $ git merge client

[[rrbdiag_g]]
.Ταχυπροώθηση του κλάδου `master` ώστε να συμπεριλάβει τις αλλαγές του κλάδου `client`.
image::images/interesting-rebase-3.png[Ταχυπροώθηση του κλάδου `master` ώστε να συμπεριλάβει τις αλλαγές του κλάδου `client`.]

Ας πείτε ότι τώρα αποφασίζετε να ενσωματώσετε και τις αλλάγες του κλάδου `server`.
Μπορείτε να αλλάξετε τη βάση του κλάδου `server` (στον κλάδο `master`), χωρίς να έχετε προηγουμένως μεταβεί σε αυτόν, εκτελώντας την εντολή `git rebase <νέα-βάση> <θεματικός-κλάδος>`, η οποία σας μεταφέρει στον θεματικό κλάδο (σε αυτή την περίπτωση, τον `server`) και εφαρμόζει τις αλλαγές του στη νέα βάση (`master`) συγχρόνως:
[source,console]

$ git rebase master server

Το αποτέλεσμα της παραπάνω εντολής φαίνεται στην <<rrbdiag_h>>.

[[rrbdiag_h]]
.Αλλαγή της βάσης του κλάδου `server` στον κλάδο `master`.
image::images/interesting-rebase-4.png[Αλλαγή της βάσης του κλάδου `server` στον κλάδο `master`.]

Στη συνέχεια μπορείτε να ταχυπροωθήσετε τον κλάδο-βάση (`master`):

[source,console]

$ git checkout master $ git merge server

Μπορείτε να αφαιρέσετε τους κλάδους `client` και `server` επειδή όλη δουλειά σας έχει ενσωματωθεί και δεν τους χρειάζεστε πια.
Κάτι τέτοιο θα κάνει το ιστορικό σας, μετά από όλη αυτή τη διαδικασία, να μοιάζει με το <<rrbdiag_i>>:

[source,console]

$ git branch -d client $ git branch -d server

[[rrbdiag_i]]
.Τελικό ιστορικό υποβολών.
image::images/interesting-rebase-5.png[Τελικό ιστορικό υποβολών.]

[[r_rebase_peril]]
==== Οι κίνδυνοι της αλλαγής βάσης

(((αλλαγή βάσης, κίνδυνοι)))
Όμως η ευδαιμονία που σας προσφέρει η αλλαγή βάσης έχει κάποιο αντίτιμο, το οποίο μπορεί να συνοψιστεί σε μία γραμμή:

*Δεν αλλάζετε τη βάση υποβολών που υπάρχουν εκτός του αποθετηρίου σας.*

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

Όταν αλλάζετε τη βάση ενός κλάδου, εγκαταλείπετε τις υπάρχουσες υποβολές και δημιουργείτε νέες που είναι παρόμοιες μεν, διαφορετικές δε.
Εάν ωθήσετε υποβολές κάπου και άλλοι τις τραβήξουν και βασίσουν τη δουλειά τους σε αυτές και στη συνέχεια ξαναγράψετε αυτές τις υποβολές με την `git rebase` και τις ωθήσετε ξανά, οι συνεργάτες σας θα πρέπει να ξανασυγχωνεύσουν τη δουλειά τους και τα πράγματα θα μπλέξουν όταν προσπαθήσετε να ελκύσετε τη δουλειά τους στη δική σας.

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

.Κλωνοποίηση αποθετηρίου και επεξεργασία του.
image::images/perils-of-rebasing-1.png[Κλωνοποίηση αποθετηρίου και επεξεργασία του.]

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

.Ανάκτηση περισσότερων υποβολών και συγχώνευσή τους στην εργασία σας.
image::images/perils-of-rebasing-2.png[Ανάκτηση περισσότερων υποβολών και συγχώνευσή τους στην εργασία σας.]

Στη συνέχεια, αυτός που ώθησε τη συγχωνευμένη δουλειά αποφασίζει να επιστρέψει και να αλλάξει τη βάση της εργασίας του· κάνει `git push --force` για να επανεγγράψει το ιστορικό στον διακομιστή.
Στη συνέχεια, ανακτάτε από τον διακομιστή και φέρνετε τις νέες υποβολές.

[[r_pre_merge_rebase_work]]
.Κάποιος ωθεί επανατοποθετημένες υποβολές, εγκαταλείποντας τις υποβολές στις οποίες έχετε βασίσει τη δουλειά σας.
image::images/perils-of-rebasing-3.png[Κάποιος ωθεί επανατοποθετημένες υποβολές, εγκαταλείποντας τις υποβολές στις οποίες έχετε βασίσει τη δουλειά σας.]

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

[[r_merge_rebase_work]]
.Συγχώνευση της ίδιας εργασίας σε μία νέα υποβολή συγχώνευσης.
image::images/perils-of-rebasing-4.png[Συγχώνευση της ίδιας εργασίας σε μία νέα υποβολή συγχώνευσης.]

Αν το ιστορικό σας μοιάζει με το παραπάνω και τρέξετε `git log`, θα δείτε δύο υποβολές που έχουν τον ίδιο συγγραφέα, ημερομηνία και μήνυμα, κάτι που μπορεί να προκαλέσει σύγχυση.
Επιπλέον, αν ωθήσετε αυτό το ιστορικό πίσω στον διακομιστή, θα επαναφέρετε όλες εκείνες τις επανατοποθετημένες υποβολές στον κεντρικό εξυπηρετητή, κάτι που θα μπερδέψει ακόμα περισσότερο τους υπόλοιπους.
Είναι αρκετά σίγουρο ότι ο άλλος προγραμματιστής δεν θέλει οι `C4` και `C6` να βρίσκονται στο ιστορικό· άλλωστε αυτός είναι ο λόγος για τον οποίο έκανε την αλλαγή της βάσης.

[[r_rebase_rebase]]
==== Επανατοποθέτηση σε επανατοποθετημένες υποβολές
Αν παρόλα αυτά, *όντως* βρεθείτε σε μια τέτοια κατάσταση, το Git διαθέτει κάποια μαγικά κόλπα που θα μπορούν να σας βοηθήσουν.
Εάν κάποιος από την ομάδα σας ωθεί εξαναγκαστικά αλλαγές που επανεγγράφουν εργασία στην οποία βασίσατε τη δική σας δουλειά, το πρόβλημά σας ανάγεται στο να εντοπίσετε τι είναι δικό σας και τι έχει ξαναγραφτεί από αυτό.

Το Git εκτός από το άθροισμα ελέγχου SHA-1 υπολογίζει επίσης ένα άθροισμα ελέγχου που βασίζεται ακριβώς στο επίθεμα που εισήχθη με την υποβολή.
Αυτό ονομάζεται "`patch-id`" (ταυτότητα επιθέματος).

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

Για παράδειγμα, αν στο προηγούμενο σενάριο, όταν βρίσκεστε στο <<r_pre_merge_rebase_work>> αντί να κάνετε συγχώνευση, τρέξετε `git rebase teamone/master`, το Git:

* Θα προσδιορίσει ποια δουλειά βρίσκεται μόνον στον κλάδο σας (`C2`, `C3`, `C4`, `C6`, `C7`)
* Θα προσδιορίσει ποιες υποβολές δεν είναι υποβολές συγχώνευσης (`C2`, `C3`, `C4`)
* Θα προσδιορίσει ποιες υποβολές δεν έχουν ξαναγραφτεί στον κλάδο στόχο (μόνο οι `C2` και `C3`, δεδομένου ότι η `C4` είναι το ίδιο επίθεμα με την `C4'`)
* Θα εφαρμόσει αυτές τις υποβολές στον κλάδο `teamone/master`.

Έτσι, αντί για το αποτέλεσμα που βλέπουμε στην <<r_merge_rebase_work>>, θα καταλήγαμε σε κάτι που μοιάζει πιο πολύ με την <<r_rebase_rebase_work>>.

[[r_rebase_rebase_work]]
.Επανατοποθέτηση πάνω σε εξαναγκασμένα επανατοποθετημένη εργασία.
image::images/perils-of-rebasing-5.png[Επανατοποθέτηση πάνω σε εξαναγκασμένα επανατοποθετημένη εργασία.]

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

Μπορείτε επίσης να απλοποιήσετε τη διαδικασία τρέχοντας μία `git pull --rebase` αντί για  κανονικό `git pull`.
Ή θα μπορούσατε να το κάνετε χειροκίνητα με μία `git fetch` ακολουθούμενο από μία `git rebase teamone/master` στη συγκεκριμένη περίπτωση.

Εάν χρησιμοποιείτε την `git pull` και θέλετε να κάνετε `--rebase` την προεπιλογή, μπορείτε να ορίσετε την τιμή της παραμέτρου `pull.rebase` με κάτι σαν `git config --global pull.rebase true`.

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

Αν αυτό θεωρηθεί απαραίτητο σε κάποιο σημείο, θα πρέπει να βεβαιωθείτε ότι όλοι έχουν ενημερωθεί να εκτελέσουν την `git pull --rebase` ώστε να καταπραΰνετε λίγο τον πόνο που θα ακολουθήσει.


==== Σύγκριση αλλαγής βάσης και συγχώνευσης

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

Μια θεώρηση του πράγματος είναι ότι το ιστορικό των υποβολών του αποθετηρίου σας είναι *καταγραφή όσων πραγματικά συνέβησαν*.
Είναι ένα ιστορικό έγγραφο, πολύτιμο από μόνο του και δεν πρέπει να παραβιάζεται.
Από αυτή τη σκοπιά, η αλλαγή του ιστορικού των υποβολών αποτελεί σχεδόν βλασφημία.
Λέτε _ψέματα_ για το τι πραγματικά συνέβη.
Τι γίνεται, λοιπόν, αν υπάρχει μια μπουρδουκλωμένη σειρά υποβολών συγχώνευσης;
Αυτός είναι ο τρόπος με τον οποίο συνέβησαν και το αποθετήριο πρέπει να τη διατηρήσει για πάντα.

Η αντίθετη άποψη είναι ότι το ιστορικό της υποβολής είναι η *ένα αφήγημα του πώς έγινε το έργο σας*.
Δεν θα δημοσιεύατε το προσχέδιο ενός βιβλίου, οπότε γιατί να δείξετε όλη thn τσαπατσούλικη δουλειά;
Όταν εργάζεστε σε ένα έργο, ίσως χρειάζεστε μια καταγραφή όλων λανθασμένων βημάτων και των αδιεξόδων, αλλά όταν φτάνει η ώρα να δείξετε τη δουλειά σας στον υπόλοιπο κόσμο, ίσως θέλετε να πείτε μια πιο συνεκτική ιστορία του πώς πήγατε από το Α στο Β.
Όσοι βρίσκονται σε αυτό το στρατόπεδο χρησιμοποιούν εργαλεία όπως `rebase` και `filter-branch` για να ξαναγράψουν τις υποβολές τους πριν αυτές συγχωνευτούν στον κύριο κλάδο.
Χρησιμοποιούν εργαλεία όπως `rebase` και `filter-branch` για να αφηγηθούν την ιστορία με αυτόν τον τρόπο που είναι ο καλύτερος για τους μελλοντικούς αναγνώστες.

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

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


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

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


[#ch04-git-server]
== Το Git στον διακομιστή

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

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

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

Ένα απομακρυσμένο αποθετήριο είναι γενικά ένα _γυμνό αποθετήριο_ -- ένα αποθετήριο Git που δεν έχει κατάλογο εργασίας.
Επειδή το αποθετήριο χρησιμοποιείται μόνο ως σημείο συνεργασίας, δεν έχει κανένα νόημα να έχει κάποιο στιγμιότυπο στον δίσκο· αποτελείται μόνο από τα δεδομένα του Git.
Πιο απλά, ένα γυμνό αποθετήριο είναι το περιεχόμενο του καταλόγου `.git` του έργου σας και τίποτα άλλο.


=== Τα πρωτόκολλα

Το Git μπορεί να χρησιμοποιήσει τέσσερα διαφορετικά πρωτόκολλα για τη μεταφορά δεδομένων: Local, HTTP, Secure Shell (SSH) και Git.
Θα συζητήσουμε τι είναι αυτά τα πρωτόκολλα και σε ποιες βασικές περιστάσεις θέλετε (ή δεν θέλετε) να τα χρησιμοποιήσετε.

==== Το τοπικό πρωτόκολλο

(((πρωτόκολλα, τοπικό)))
Το πιο βασικό είναι το _τοπικό πρωτόκολλο_, στο οποίο το απομακρυσμένο αποθετήριο βρίσκεται σε άλλο κατάλογο στον δίσκο.
Αυτό χρησιμοποιείται συχνά εάν όλοι στην ομάδα σας έχουν πρόσβαση σε ένα κοινό σύστημα αρχείων (filesystem), όπως https://en.wikipedia.org/wiki/Network_File_System[NFS^] mount ή στη σχετικά σπάνια περίπτωση που όλοι οι χρήστες συνδέονται στον ίδιο υπολογιστή.
Η τελευταία περίπτωση είναι λιγότερο από ιδανική, διότι όλα τα στιγμιότυπα κώδικα του αποθετηρίου κατοικούν στον ίδιο υπολογιστή, καθιστώντας πολύ πιο πιθανή μια καταστροφική απώλεια.

Εάν διαθέτετε ένα κοινόχρηστο σύστημα αρχείων, μπορείτε να κλωνοποιήσετε, να ωθήσετε σε και να ελκύσετε από ένα αποθετήριο που βασίζεται σε τοπικά αρχεία.
Για να κλωνοποιήσετε ένα αποθετήριο όπως αυτό ή για να το προσθέσετε ως απομακρυσμένο σε ένα υπάρχον έργο, χρησιμοποιείτε τη διαδρομή (path) του αποθετηρίου ως διεύθυνση URL.
Για παράδειγμα, για να κλωνοποιήσετε ένα τοπικό αποθετήριο, μπορείτε να εκτελέσετε κάτι σαν:

[source,console]

$ git clone /srv/git/project.git

Ή κάτι σαν:

[source,console]
Το Git λειτουργεί ελαφρώς διαφορετικά αν καθορίσετε ρητά το `file://` στην αρχή της διεύθυνσης URL.
Αν καθορίσετε μόνο τη διαδρομή, το Git προσπαθεί να χρησιμοποιήσει hardlinks ή να αντιγράψει απευθείας τα αρχεία που χρειάζονται.
Εάν προσθέσετε το `file://`, το Git ενεργοποιεί τις διαδικασίες που συνήθως χρησιμοποιεί για τη μεταφορά δεδομένων μέσω δικτύου, μία μέθοδο μεταφοράς των δεδομένων γενικά πολύ λιγότερο αποτελεσματική.
Ο βασικός λόγος που θα θέλαμε να χρησιμοποιήσετε το `file://` είναι η περίπτωση κατά την οποία θέλετε ένα καθαρό αντίγραφο του αποθετηρίου με εξωτερικές αναφορές ή αντικείμενα που απομένουν -- συνήθως μετά από εισαγωγή από ένα άλλο σύστημα ελέγχου εκδόσεων ή κάτι παρόμοιο (βλ. <<ch10-git-internals>> για σχετικές εργασίες συντήρησης).
Στο παρακάτω παράδειγμα θα χρησιμοποιήσουμε τη διαδρομή χωρίς το `file://` επειδή αυτό είναι σχεδόν πάντα γρηγορότερο.

Για να προσθέσετε ένα τοπικό αποθετήριο σε ένα υπάρχον έργο Git, μπορείτε να εκτελέσετε κάτι σαν:

[source,console]

$ git remote add local_proj /srv/git/project.git

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

===== Τα υπέρ

Τα πλεονεκτήματα των αποθετηρίων που βασίζονται σε αρχεία είναι ότι είναι απλά και χρησιμοποιούν τα υπάρχοντα δικαιώματα σε αρχεία και πρόσβαση στο δίκτυο.
Εάν έχετε ήδη ένα κοινό σύστημα αρχείων στο οποίο έχει πρόσβαση ολόκληρη η ομάδα σας, η εγκατάσταση ενός αποθετηρίου είναι πολύ εύκολη.
Μπορείτε να κολλήσετε ένα γυμνό (χωρίς αρχεία) αντίγραφο του αποθετηρίου κάπου όπου ο καθένας έχει πρόσβαση και να ορίσετε τα δικαιώματα ανάγνωσης/εγγραφής όπως θα κάνατε για οποιονδήποτε άλλο κοινόχρηστο κατάλογο.
Θα συζητήσουμε πώς μπορείτε να κάνετε export ένα αντίγραφο γυμνού αποθετηρίου για αυτόν τον σκοπό αυτό στο <<r_git_on_the_server>>.

Αυτή είναι επίσης μια καλή επιλογή για γρήγορη λήψη της εργασίας από το αποθετήριο εργασίας κάποιου άλλου.
Εάν εσείς και μία συνεργάτιδά σας εργάζεστε στο ίδιο έργο και η συνεργάτιδά σας θέλει να ελέγξετε τη δουλειά της, η εκτέλεση μιας εντολής όπως `git pull /home/john/project` είναι συχνά ευκολότερη από ό,τι η συνεργάτιδά σας να ωθήσει σε έναν απομακρυσμένο διακομιστή και εσείς να ανακτήσετε από αυτόν.

===== Τα κατά

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

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

Τέλος, το πρωτόκολλο αυτό δεν προστατεύει το αποθετήριο από τυχαίες αστοχίες.
Όλοι οι χρήστες έχουν πλήρη πρόσβαση στο κέλυφος στο "`απομακρυσμένο`" αποθετήριο και τίποτα δεν τους εμποδίζει να αλλάξουν ή να αφαιρέσουν εσωτερικά αρχεία του Git και να καταστρέψουν το αποθετήριο.

==== Πρωτόκολλα HTTP

Το Git μπορεί να επικοινωνήσει μέσω HTTP με δύο διαφορετικούς τρόπους.
Πριν από το Git 1.6.6 υπήρχε μόνον ένας τρόπος, που ήταν πολύ απλοϊκός και γενικά μόνο-για-ανάγνωση.
Στην έκδοση 1.6.6 εισήχθη ένα νέο, πιο έξυπνο πρωτόκολλο το οποίο περιλάμβανει τη δυνατότητα του Git να διαπραγματεύεται έξυπνα τη μεταφορά δεδομένων με τρόπο παρόμοιο όπως μέσω SSH.
Τα τελευταία χρόνια αυτό το νέο πρωτόκολλο HTTP έχει γίνει πολύ δημοφιλές, διότι είναι απλούστερο για τους χρήστες και πιο έξυπνο όσον αφορά στον τρόπο επικοινωνίας.
Η νεότερη έκδοση αναφέρεται συχνά ως "`έξυπνο`" HTTP και ο παλαιότερος τρόπος ως "`χαζό`" HTTP.
Θα καλύψουμε πρώτα το πιο πρόσφατο "`έξυπνο`" HTTP.

===== Έξυπνο HTTP

(((πρωτόκολλα, έξυπνο HTTP)))
Το "`έξυπνο`" HTTP έχει παρόμοια λειτουργία με τα πρωτόκολλα SSH ή Git, αλλά τρέχει στις θύρες που χρησιμοποιουνται τυπικά για HTTPS και μπορεί να χρησιμοποιήσει διάφορους μηχανισμούς ταυτοποίησης HTTP, κάτι που σημαίνει ότι είναι συχνά πιο εύχρηστο για τους χρήστες από ό,τι είναι για παράδειγμα το SSH, δεδομένου ότι επιτρέπει την ταυτοποίηση με όνομα χρήστη/κωδικό πρόσβασης, αντί για κλειδιά SSH.

Φαίνεται ότι πλέον είναι ο πιο δημοφιλής τρόπος χρήσης του Git, αφού μπορεί να ρυθμιστεί τόσο για να ανώνυμη ανάκτηση όπως κάνει το πρωτόκολλο `git://` όσο και για ώθηση με ταυτοποίηση και κρυπτογράφηση όπως το πρωτόκολλο SSH.
Αντί να πρέπει να ορίσετε διαφορετικές διευθύνσεις URL για αυτά τα δύο πράγματα, μπορείτε πλέον να χρησιμοποιήσετε μια ενιαία διεύθυνση URL και για τα δύο.
Αν προσπαθήσετε να ωθήσετε και το αποθετήριο απαιτεί ταυτοποίηση (όπως κανομικά πρέπει να κάνει), ο διακομιστής μπορεί να ζητήσει όνομα χρήστη και κωδικό πρόσβασης.
Το ίδιο ισχύει και για την πρόσβαση ανάγνωσης.

Μάλιστα, για υπηρεσίες όπως το GitHub, η διεύθυνση URL που χρησιμοποιείτε για την προβολή του αποθετηρίου online (για παράδειγμα, `https://github.com/schacon/simplegit[^]`) είναι η ίδια διεύθυνση URL που μπορείτε να χρησιμοποιήσετε για να κλωνοποιήσετε και, εφόσον έχετε πρόσβαση, να ωθήσετε.


===== Χαζό HTTP

(((πρωτόκολλα, χαζό HTTP)))
Εάν ο διακομιστής δεν ανταποκρίνεται σε μια υπηρεσία έξυπνου HTTP του Git, ο πελάτης θα προσπαθήσει να χρησιμοποιήσει το απλούστερο πρωτόκολλο "`χαζό`" HTTP.
Το χαζό πρωτόκολλο αναμένει ότι το γυμνό αποθετήριο Git να εξυπηρετείται σαν να επρόκειτο για κανονικά αρχεία από τον web server.
Η ομορφιά του χαζού HTTP είναι η απλότητα στην εγκατάστασή του.
Βασικά, το μόνο που έχετε να κάνετε είναι να τοποθετήσετε ένα γυμνό αποθετήριο Git κάτω από τον ριζικό κατάλογο του εγγράφου HTTP και να δημιουργήσετε ένα συγκεκριμένο άγκιστρο `post-update`, και τελειώσατε (βλ. <<r_git_hooks>>).
Σε αυτό το σημείο, οποιοσδήποτε έχει πρόσβαση στον web server κάτω από τον οποίο έχετε κρεμάσει το αποθετήριο, μπορεί επίσης να κλωνοποιήσει το αποθετήριο.
Για να επιτρέψετε πρόσβαση ανάγνωσης στο αποθετήριό σας μέσω HTTP, κάνετε κάτι σαν αυτό:

[source,console]

$ cd /var/www/htdocs/ $ git clone --bare /path/to/git_project gitproject.git $ cd gitproject.git $ mv hooks/post-update.sample hooks/post-update $ chmod a+x hooks/post-update

Αυτό είναι όλο. (((άγκιστρα, post-update)))
Το άγκιστρο `post-update` (που έρχεται με το Git) τρέχει την κατάλληλη εντολή (`git update-server-info`) για να κάνει την ανάκτηση και κλωνοποίηση μέσω HTTP να λειτουργούν σωστά.
Αυτή η εντολή εκτελείται όταν ωθείτε σε αυτό το αποθετήριο (ίσως μέσω SSH)· τότε, κάποιος μπορεί να κλωνοποιήσει με κάτι σαν:

[source,console]
Στη συγκεκριμένη περίπτωση, χρησιμοποιείτε τη διαδρομή `/var/www/htdocs` που είναι κοινή για διακομιστές Apache, αλλά μπορείτε να χρησιμοποιήσετε οποιονδήποτε στατικό web server -- απλά τοποθετείτε το γυμνό αποθετήριο στη διαδρομή του.
Τα δεδομένα Git διακομίζονται ως απλά στατικά αρχεία (βλ. ενότητα <<ch10-git-internals>> για λεπτομέρειες σχετικά με τον τρόπο με τον οποίο διακομίζονται).

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

===== Τα υπέρ

Θα επικεντρωθούμε στα πλεονεκτήματα της έξυπνης έκδοσης του πρωτοκόλλου HTTP.

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

Μπορείτε επίσης να διαθέτετε τα αποθετήριά σας μόνο για ανάγνωση μέσω HTTPS, πράγμα που σημαίνει ότι μπορείτε να κρυπτογραφήσετε τη μεταφορά του περιεχομένου· ή μπορείτε να ακόμα να κάνετε τους πελάτες να χρησιμοποιούν ειδικά υπογεγραμμένα πιστοποιητικά SSL.

Ένα άλλο υπέρ είναι ότι το HTTPS είναι τόσο διαδεδομένο πρωτόκολλο που συχνά τα εταιρικά firewall ρυθμίζονται με τέτοιον τρόπο ώστε να επιτρέπουν την κίνηση δεδομένων μέσω αυτών των θυρών.

===== Τα κατά

Το Git μέσω HTTPS εγκαθίσταται λίγο πιο δύσκολα σε σύγκριση με το SSH σε ορισμένους διακομιστές.
Εκτός από αυτό, τα άλλα πρωτόκολλα δεν έχουν κανένα σημαντικό πλεονέκτημα σε σύγκριση με το πρωτόκολλο "`έξυπνο`" HTTP όσον αφορά στο Git.

Αν χρησιμοποιείτε HTTP για ταυτοποίηση ώθησης, η παροχή των διαπιστευτηρίων είναι κάποιες φορές πιο πολύπλοκη από τη χρήση κλειδιών SSH.
Ωστόσο, υπάρχουν αρκετά εργαλεία προσωρινής αποθήκευσης διαπιστευτηρίων που μπορείτε να χρησιμοποιήσετε, συμπεριλαμβανομένων των Keychain στο OS X και Credential Manager στα Windows, που καθιστούν τη διαδικασία ταυτοποίησης αρκετά ανώδυνη.
Στην ενότητα <<r_credential_caching>> μπορείτε να δείτε πώς μπορείτε να ρυθμίσετε ασφαλή προσωρινή αποθήκευση κωδικού πρόσβασης HTTP στο σύστημά σας.

==== Το πρωτόκολλο SSH

(((πρωτόκολλα, SSH)))
Ένα κοινό πρωτόκολλο μεταφοράς για το Git όταν αυτο-φιλοξενείται είναι το SSH.
Αυτό οφείλεται στο ότι η πρόσβαση σε διακομιστές μέσω SSH είναι ήδη ρυθμισμένη -- και αν δεν είναι, είναι εύκολο να γίνει.
Το SSH είναι επίσης πρωτόκολλο ταυτοποιημένου δικτύου και επειδή είναι πανταχού παρόν είναι γενικά εύκολο να εγκαταστασθεί και να χρησιμοποιηθεί.

Για να κλωνοποιήσετε ένα αποθετήριο Git πάνω από SSH, μπορείτε να ορίσετε το URL `ssh://` ως εξής:

[source,console]

$ git clone ssh://[user@]server/project.git

Ή μπορείτε να χρησιμοποιήσετε τη συντομότερη σύνταξη τύπου scp για το πρωτόκολλο SSH:

[source,console]

$ git clone [user@]server:project.git

Και στις δύο περιπτώσεις, αν δεν ορίσετε το όνομα χρήστη, το Git χρησιμοποιεί το όνομα χρήστη που έχετε στο σύστημά σας.

===== Τα υπέρ

Τo SSH έχει πολλά πλεονεκτήματα
Κατ' αρχάς το SSH είναι σχετικά εύκολο να εγκατασταθεί -- οι δαίμονες SSH είναι πολύ συνηθισμένοι, πολλοί διαχειριστές δικτύου έχουν εμπειρία με αυτούς και πολλά λειτουργικά συστήματα είναι εγκατεστημένα με αυτούς ή έχουν εργαλεία να τους διαχειρίζονται.
Ακόμα, η πρόσβαση μέσω SSH είναι ασφαλής -- όλη η μεταφορά δεδομένων είναι κρυπτογραφημένη και απαιτεί ταυτοποίηση.
Τέλος, όπως και το HTTPS, το Git και το πρωτόκολο local, το SSH είναι αποδοτικό με την έννοια ότι συμπιέζει τα δεδομένα όσο είναι δυνατό πριν τη μεταφορά.

===== Τα κατά

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

==== Το πρωτόκολλο Git

(((protocols, git)))
Τέλος, έχουμε το πρωτόκολλο Git.
Το πρωτόκολλο Git είναι ένας ειδικός δαίμονας που έρχεται μαζί με το Git· ακούει στη θύρα 9418 που παρέχει μία υπηρεσία παρόμοια με το πρωτόκολλο SSH αλλά με απολύτως καμία ταυτοποίηση ή κρυπτογράφηση.
Για να διαθέσετε ένα αποθετήριο πάνω από το πρωτόκολλο Git, πρέπει να δημιουργήσετε το αρχείο `git-daemon-export-ok` -- ο δαίμονας δεν θα σερβίρει το αποθετήριο αν δεν έχει αυτό το αρχείο -- αλλά πέρα από αυτό δεν υπάρχει καμία ασφάλεια.
Είτε το αποθετήριο Git είναι διαθέσιμο σε όλους να το κλωνοποιήσουν είτε σε κανέναν.
Αυτό σημαίνει ότι γενικά δεν γίνεται ώθηση πάνω από αυτό το πρωτόκολλο.
Μπορείτε να ενεργοποιήσετε την πρόσβαση ώθησης, αλλά δεδομένης της έλλειψης ταυτοποίησης αν ενεργοποιήσετε την πρόσβαση ώθησης, οποιοσδήποτε βρίσκει το URL του έργου σας στο Internet, θα μπορεί να ωθήσει σε αυτό.
Είναι προφανές ότι αυτή η συμπεριφορά είναι σπάνια επιθυμητή.

===== Τα υπέρ

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

===== Τα κατά

Λόγω της έλλειψης TLS ή άλλης κρυπτογράφησης, η κλωνοποίηση μέσω `git://` μπορεί να οδηγήσει σε αυθαίρετο κενό ασφάλειας εκτέλεσης κώδικα και συνεπώς πρέπει να αποφεύγεται εκτός κι αν γνωρίζετε πολύ καλά τι κάνετε.

* Αν εκτελέσετε `git clone git://example.com/project.git`, κάποιος κακόβουλος που ελέγχει, π.χ. τον router σας μπορεί να τροποποιήσει το αποθετήριο που μόλις κλωνοποιήσατε, εισάγοντας κακόβουλο κώδικα σε αυτό.
  Αν μετά μεταγλωττίσετε/εκτελέσετε τον κώδικα που μόλις κλωνοποιήσατε, θα εκτελέσετε τον κακόβουλο κώδικα.
  Η εκτέλεση της εντολής `git clone http://example.com/project.git` πρέπει να αποφεύγεται για τον ίδιο λόγο.
* Η εκτέλεση της εντολής `git clone https://example.com/project.git` δεν παρουσιάζει το ίδιο πρόβλημα (εκτός κι αν ο κακόβουλος χρήστης παράσχει πιστοποιητικό TLS για το example.com).
  Η εκτέλεση της εντολής `git clone git@example.com:project.git` παρουσιάζει αυτό το πρόβλημα αν αποδεχτείτε ένα λανθασμένο αποτύπωμα κλειδιού SSH.

Επίσης δεν έχει ταυτοποίηση, δηλαδή ο καθένας μπορεί να κλωνοποιήσει το αποθετήριο (αν και συχνά αυτό ακριβώς θέλετε).
Επίσης είναι πιθανότατα το πιο δύσκολο πρωτόκολλο από πλευράς ρύθμισης.
Πρέπει να τρέχει το δικό του δαίμονα, που απαιτεί ρύθμιση `xinetd` ή `systemd` ή κάτι παρόμοιο, το οποίο δεν είναι πάντα εύκολο.
Απαιτεί επίσης πρόσβαση στη θύρα 9418 του firewall, που δεν είναι κάποια από τις τυποποιημένες θύρες που επιτρέπουν τα firewall των εταιρικών δικτύων.
Πίσω από τα firewall μεγάλων εταιριών, αυτή η ασυνήθιστη θύρα είναι συνήθως μπλοκαρισμένη.



[[r_git_on_the_server]]
=== Εγκατάσταση του Git σε διακομιστή

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

[NOTE]
====
Εδώ θα επιδείξετε τις εντολές και τα βήματα που απαιτούνται για να κάνετε βασικές απλοποιημένες εγκαταστάσεις σε έναν διακομιστή σε Linux, αν και είναι επίσης δυνατή η εκτέλεση αυτών των υπηρεσιών σε διακομιστές Mac ή Windows.
Η εγκατάσταση ενός διακομιστή παραγωγής στην υποδομή σας προϋποθέτει ασφαλώς διαφορές στα μέτρα ασφαλείας ή τα εργαλεία των λειτουργικών συστημάτων, αλλά αυτό θα σας δώσει τη γενική ιδέα για το τι εμπλέκεται.
====

Για να ρυθμίσετε αρχικά οποιοδήποτε διακομιστή Git, πρέπει να εξάγετε ένα υπάρχον αποθετήριο σε ένα νέο γυμνό αποθετήριο -- ένα αποθετήριο που δεν περιέχει κατάλογο εργασίας.
Αυτό είναι γενικά εύκολο να γίνει.
Για να κλωνοποιήσετε το αποθετήριό σας και να δημιουργήσετε ένα νέο γυμνό αποθετήριο, εκτελείτε την εντολή `clone` με την επιλογή `--bare`. (((εντολές git, clone, bare)))
Συμβατικά, οι γυμνοί κατάλογοι αποθετηρίων τελειώνουν σε `.git`, ως εξής:

[source,console]

$ git clone --bare my_project my_project.git Cloning into bare repository my_project.git…​ done.

Θα πρέπει τώρα να έχετε ένα αντίγραφο των δεδομένων του καταλόγου Git στον κατάλογο `my_project.git`.

Αυτό είναι σχεδόν ισοδύναμο με κάτι σαν αυτό:

[source,console]

$ cp -Rf my_project/.git my_project.git

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


[[r_bare_repo]]
==== Τοποθέτηση του γυμνού αποθετηρίου σε έναν διακομιστή

Τώρα που έχετε ένα γυμνό αντίγραφο του αποθετηρίου σας, το μόνο που χρειάζεται να κάνετε είναι να το βάλετε σε έναν διακομιστή και να ρυθμίσετε τα πρωτόκολλά σας.
Ας υποθέσουμε ότι έχετε δημιουργήσει έναν διακομιστή που ονομάζεται `git.example.com` στον οποίο έχετε πρόσβαση μέσω SSH και θέλετε να αποθηκεύσετε όλα τα αποθετήρια Git στον κατάλογο `/opt/git`.
Υποθέτοντας ότι το `/opt/git` υπάρχει σε αυτόν τον διακομιστή, μπορείτε να ρυθμίσετε το νέο αποθετήριό σας αντιγράφοντας το γυμνό αποθετήριο:

[source,console]

$ scp -r my_project.git user@git.example.com:/srv/git

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

[source,console]

$ git clone user@git.example.com:/srv/git/my_project.git

Αν ένας χρήστης συνδεθεί με SSH σε έναΝ διακομιστή και έχει δικαίωμα εγγραφής στον κατάλογο `/srv/git/my_project.git`, θα έχει αυτόματα δικαίωμα ώθησης.

Το Git θα προσθέσει αυτόματα δικαιώματα εγγραφής σε ομάδες σε ένα αποθετήριο σωστά εάν εκτελέσετε την εντολή `git init` με την επιλογή `--shared`. (((εντολές git, init, bare)))

[source,console]

$ ssh user@git.example.com $ cd /srv/git/my_project.git $ git init --bare --shared

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

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

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

==== Μικρές εγκαταστάσεις

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

===== Πρόσβαση μέσω SSH

(((serving repositories, SSH)))
Εάν έχετε έναν διακομιστή στον οποίο όλοι οι προγραμματιστές σας έχουν ήδη πρόσβαση SSH, είναι γενικά ευκολότερο να δημιουργήσετε το πρώτο αποθετήριό σας σε αυτόν, επειδή δεν χρειάζεται να κάνετε σχεδόν καθόλου δουλειά (όπως συζητήθηκε στην προηγούμενη ενότητα).
Αν θέλετε πιο πολύπλοκα είδη δικαιωμάτων ελέγχου πρόσβασης στα αποθετήριά σας, μπορείτε να τα χειριστείτε με τα δικαιώματα συστήματος αρχείων του λειτουργικού συστήματος που τρέχει ο διακομιστής σας.

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

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

Μια δεύτερη μέθοδος είναι να δημιουργήσετε έναν μοναδικό χρήστη 'git' στο μηχάνημα, να ζητήσετε από κάθε χρήστη που θα έχει πρόσβαση εγγραφής να σας στείλει ένα δημόσιο κλειδί SSH και να προσθέσετε αυτό το κλειδί στο αρχείο `~/.ssh/authorized_keys` του νέου χρήστη 'git'.
Σε αυτό το σημείο, όλοι θα μπορούν να έχουν πρόσβαση σε αυτό το μηχάνημα μέσω του χρήστη 'git'.
Αυτό δεν επηρεάζει τα δεδομένα των υποβολών με κανέναν τρόπο -- ο χρήστης SSH που συνδέεται δεν επηρεάζει τις υποβολές που έχετε καταγράψει.

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


[[r_generate_ssh_key]]
=== Δημιουργία δημόσιου κλειδιού SSH

(((SSH keys)))
Πολλοί διακομιστές Git ταυτοποιούν τους χρήστες χρησιμοποιώντας δημόσια κλειδιά SSH.
Για να παρέχετε δημόσιο κλειδί, όλοι οι χρήστες στο σύστημά σας πρέπει να δημιουργήσουν ένα, αν δεν έχουν ήδη.
Η διαδικασία είναι παρόμοια σε όλα τα λειτουργικά συστήματα.
Πρώτα πρέπει να ελέγξετε ότι δεν έχετε ήδη κλειδί.
Η προεπιλεγμένη θέση στην οποία αποθηκεύονται τα κλειδιά SSH ενός χρήστη είναι ο κατάλογος  `~/.ssh`.
Μπορείτε εύκολα να ελέγξετε αν έχετε ήδη κλειδί πηγαίνοντας σε αυτόν τον κατάλογο και βλέποντας τα περιεχόμενά του:

[source,console]

$ cd ~/.ssh $ ls authorized_keys2 id_dsa known_hosts config id_dsa.pub

Ψάχνετε για ένα αρχείο που ονομάζεται `id_dsa` ή `id_rsa` ή κάτι παραπλήσιο και ένα ακόμα με το ίδιο όνομα αλλά κατάληξη `.pub`.
Το αρχείο με κατάληξη `.pub` είναι το δημόσιο κλειδί σας και το άλλο αρχείο είναι το ιδιωτικό κλειδί σας.
Αν δεν έχετε τέτοια αρχεία (ή αν δεν έχετε καν κατάλογο `.ssh`), μπορείτε να τα δημιουργήσετε τρέχοντας ένα πρόγραμμα που ονομάζεται `ssh-keygen` και το οποίο παρέχεται με το πακέτο SSH σε συστήματα Linux και Mac και με το Git στα Windows:

[source,console]

ssh-keygen -o Generating public/private rsa key pair. Enter file in which to save the key (/home/schacon/.ssh/id_rsa): Created directory /home/schacon/.ssh. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/schacon/.ssh/id_rsa. Your public key has been saved in /home/schacon/.ssh/id_rsa.pub. The key fingerprint is: d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local

Πρώτα επιβεβαιώνει πού έχετε αποθηκεύσει το κλειδί (`.ssh/id_rsa`) και μετά ρωτά δύο φορές για μία κωδική φράση, την οποία μπορείτε να αφήσετε κενή, εφόσον δεν θέλετε να πληκτρολογείτε κωδικό πρόσβασης κάθε φορά που χρησιμοποιείτε το κλειδί.
Αν πάντως θέλετε να χρησιμοποιείτε κωδικό πρόσβασης, σιγουρευτείτε ότι έχετε προσθέσει την επιλογή `-o`, ώστε το ιδιωτικό κλειδί να αποθηκευτεί σε τέτοια μορφή που να αντιστέκεται στην παραβίαση του κωδικού πρόσβασης περισσότερο από ότι η προεπιλεγμένη μορφή.
Μπορείτε επίσης να χρησιμοποιήσετε το εργαλείο `ssh-agent` για να μη χρειάζεται να εισάγετε τον κωδικό πρόσβασης κάθε φορά.

Τώρα κάθε χρήστης που κάνει αυτή τη διαδικασία πρέπει να στείλει το δημόσιο κλειδί του σε σας ή σε όποιον είναι ο διαχειριστής του διακομιστή Git (με την προϋπόθεση ότι χρησιμοποιείτε έναν διακομιστή με SSH που απαιτεί δημόσια κλειδιά).
Το μόνο που πρέπει να κάνει είναι να αντιγράψει τα περιεχόμενα του αρχείου `.pub` και να τα στείλει με e-mail.
Το δημόσιο κλειδί μοιάζει με κάτι τέτοιο:

[source,console]

$ cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3 Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx NrRFi9wrf+M7Q== schacon@mylaptop.local

Για έναν οδηγό σε μεγαλύτερο βάθος σχετικά με τη δημιουργία κλειδιού SSH σε διάφορα λειτουργικά συστήματα, βλ. τον οδηγό του GitHub για τα κλειδιά SSH στην ιστοσελίδα https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent[^].


[[r_setting_up_server]]
=== Στήσιμο του διακομιστή

Ας δούμε τώρα τη διαμόρφωση της πρόσβασης SSH από την πλευρά του διακομιστή.
Σε αυτό το παράδειγμα θα χρησιμοποιήσετε τη μέθοδο `authorized_keys` για την ταυτοποίηση των χρηστών.
Υποθέτουμε ότι τρέχετε μια τυπική διανομή Linux όπως το Ubuntu.

[NOTE]
====
Πολλά από όσα περιγράφονται εδώ, μπορείτε να τα αυτοματοποιήσετε με την εντολή `ssh-copy-id`, αντί να αντιγράφετε και εγκαθιστάτε δημόσια κλειδιά χειροκίνητα.
====

Καταρχάς, δημιουργείτε ένα χρήστη `git` και έναν κατάλογο `.ssh` για αυτόν τον χρήστη.

[source,console]

$ sudo adduser git $ su git $ cd $ mkdir .ssh && chmod 700 .ssh $ touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys

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

[source,console]

$ cat /tmp/id_rsa.john.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L ojG6rs6hPB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4k Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myiv O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq dAv8JggJICUvax2T9va5 gsg-keypair

Απλά τα προσθέτετε στο τέλος του αρχείου `authorized_keys` του χρήστη `git` στον κατάλογο `.ssh` του χρήστη:

[source,console]

$ cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys $ cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys $ cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys

Τώρα, μπορείτε να δημιουργήσετε ένα κενό αποθετήριο για αυτό τον χρήστη, τρέχοντας `git init` με την επιλογή `--bare`, η οποία αρχικοποιεί το αποθετήριο χωρίς κατάλογο εργασίας: (((εντολές git, init, bare)))

[source,console]

$ cd /opt/git $ mkdir project.git $ cd project.git $ git init --bare Initialized empty Git repository in /srv/git/project.git/

Στη συνέχεια, ο Τζον, η Τζόσι ή η Τζέσικα μπορούν να ωθήσουν την πρώτη έκδοση του έργου τους σε αυτό το αποθετήριο, προσθέτοντάς το ως απομακρυσμένο και ωθώντας έναν κλάδο.
Σημειώστε ότι κάποιος πρέπει να μπαίνει στο κέλυφος στο συγκεκριμένο μηχάνημα και να δημιουργεί ένα γυμνό αποθετήριο κάθε φορά που θέλετε να προσθέσετε ένα έργο.
Ας χρησιμοποιήσουμε το `gitserver` ως το hostname του διακομιστή στον οποίο έχετε δημιουργήσει τον χρήστη `git` και το αποθετήριο.
Αν το τρέξετε εσωτερικά και έχετε ρυθμίσει το DNS για το `gitserver` να δείχνει σε εκείνον τον διακομιστή, τότε μπορείτε να χρησιμοποιήσετε τις εντολές σχεδόν όπως είναι (με την προϋπόθεση ότι το `myproject` είναι ένα υπάρχον έργο με αρχεία):

[source,console]

# on Johns computer $ cd myproject $ git init $ git add . $ git commit -m initial commit $ git remote add origin git@gitserver:/srv/git/project.git $ git push origin master

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

[source,console]

$ git clone git@gitserver:/srv/git/project.git $ cd project $ vim README $ git commit -am fix for the README file $ git push origin master

Με αυτή τη μέθοδο, μπορείτε να ξεκινήσετε γρήγορα έναν διακομιστή Git ανάγνωσης/εγγραφής για μερικούς προγραμματιστές.

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

Μπορείτε να περιορίσετε εύκολα τον χρήστη `git` ώστε να κάνει μόνο δραστηριότητες Git με ένα εργαλείο περιορισμένου κελύφους που ονομάζεται `git-shell` και διατίθεται στο Git.
Αν ορίσετε αυτό ως το κέλυφος σύνδεσης του χρήστη `git`, τότε ο χρήστης `git` δεν μπορεί να έχει κανονική πρόσβαση μέσω του κελύφους στον διακομιστή σας.
Για να το χρησιμοποιήσετε, καθορίστε `git-shell` αντί για `bash` ή `csh` για το κέλυφος σύνδεσης του χρήστη.
Για να συμβεί αυτό, πρέπει προηγουμένως να προσθέσετε το `git-shell` στο `/etc/shells` αν δεν υπάρχει ήδη:

[source,console]

$ cat /etc/shells # δες αν το git-shell βρίσκεται ήδη εκεί. Αν όχι…​ $ which git-shell # επιβεβαίωσε ότι το git-shell είναι εγκατεστημένο στο σύστημα $ sudo vim /etc/shells # και πρόσθεσε τη διαδρομή για το git-shell από την προηγούμενη εντολή

Τώρα μπορείτε να επεξεργαστείτε το κέλυφος για έναν χρήστη χρησιμοποιώντας `chsh <όνομα-χρήστη>` -s <κέλυφος>`:

[source,console]

$ sudo chsh git -s $(which git-shell)

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

[source,console]

$ ssh git@gitserver fatal: Interactive git shell is not enabled. hint: ~/git-shell-commands should exist and have read and execute access. Connection to gitserver closed.

Σε αυτό το σημείο, οι χρήστες ακόμα μπορούν να χρησιμοποιήσουν πρόωθηση θύρας SSH ώστε να αποκτήσουν πρόσβαση σε οποιονδήποτε host μπορεί να φτάσει ο διακομιστής Git.
Αν δεν θέλετε να γίνεται αυτό, μπορείτε να επεξεργαστείτε το αρχείο `authorized_keys` και να γράψετε πριν από κάθε κλειδί που θέλετε να περιορίσετε το εξής

[source,console]

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty

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

[source,console]

$ cat ~/.ssh/authorized_keys no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4LojG6rs6h PB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4kYjh6541N YsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9EzSdfd8AcC IicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myivO7TCUSBd LQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPqdAv8JggJ ICUvax2T9va5 gsg-keypair

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEwENNMomTboYI+LJieaAY16qiXiH3wuvENhBG…​

Τώρα οι εντολές δικτύου του Git θα λειτουργούν ακόμα μια χαρά, αλλά οι χρήστες δεν θα μπορούν να ανοίξουν κάποιο κέλυφος.
Όπως αναφέρει και η έξοδος της εντολής, μπορείτε επίσης να ρυθμίσετε έναν κατάλογο στον αρχικό κατάλογο του χρήστη `git` που προσαρμόζει ελαφρώς την εντολή `git-shell`.
Για παράδειγμα, μπορείτε να περιορίσετε τις εντολές Git που μπορεί να δέχεται ο διακομιστής ή να προσαρμόσετε το μήνυμα που βλέπουν οι χρήστες αν προσπαθούν να κάνουν SSH με αυτόν τον τρόπο.
Εκτελέστε `git shell help` για περισσότερες πληροφορίες σχετικά με την προσαρμογή του κελύφους στις προτιμήσεις σας. (((εντολές git, help)))



=== Δαίμονες του Git

(((αποθετήρια εξυπηρέτησης, πρωτόκολλο git)))
(((serving repositories, git protocol)))
Στη συνέχεια θα εγκαταστήσΟυμε έναν δαίμονα που θα εξυπηρετεί αποθετήρια μέσω του πρωτοκόλλου "Git".
Αυτή είναι μια συνήθης επιλογή για γρήγορη και χωρίς ταυτοποίηση πρόσβαση στα δεδομένα σας στο Git.
Να θυμάστε πως δεδομένου ότι πρόκειται για μια υπηρεσία χωρίς ταυτοποίηση, οτιδήποτε παρέχεται πάνω από αυτό το πρωτόκολλο είναι δημόσιο εντός του δικτύου του.

Εάν τρέχετε τον δαίμονα σε έναν διακομιστή εκτός firewall, θα πρέπει να χρησιμοποιείται μόνο για έργα που είναι ορατά σε όλον τον κόσμο.
Αν ο διακομιστής στον οποίο τον τρέχετε είναι εντός firewall, μπορείτε να τον χρησιμοποιήσετε για έργα στα οποία ένας μεγάλος αριθμός ανθρώπων ή υπολογιστών (continuous integration ή build servers) έχουν πρόσβαση μόνο για ανάγνωση, όταν δεν θέλετε να προσθέσετε κλειδί SSH για τον καθένα.

Σε κάθε περίπτωση, το πρωτόκολλο Git είναι σχετικά εύκολο στη ρύθμισή του.
Βασικά, θα πρέπει να εκτελέσετε αυτή την εντολή με "`δαιμονοποιημένο`" τρόπο: (((εντολές git, δαίμονας)))

[source,console]

git daemon --reuseaddr --base-path=/srv/git/ /srv/git/

Ο διακόπτης `--reuseaddr` επιτρέπει στον διακομιστή να επανεκκινήσει χωρίς να αναμένει αποσύνδεση των παλαιών συνδέσεων, ο διακόπτης `--base-path` επιτρέπει την κλωνοποίηση έργων χωρίς να καθορίζεται ολόκληρη τη διαδρομή και η διαδρομή στο τέλος λέει στον δαίμονα Git πού να αναζητήσει αποθετήρια προς εξαγωγή.
Εάν τρέχετε ένα firewall, θα χρειαστεί επίσης να του ανοίξετε μία τρύπα στη θύρα 9418 στο κουτί που τον εγκαθιστάτε τον διακομιστή.

Μπορείτε να "`δαιμονοποίησετε`" αυτή τη διαδικασία με διάφορους τρόπους, ανάλογα με το λειτουργικό σύστημα που εκτελείτε.
Δεδομένου ότι `systemd` είναι το πιο συνηθισμένο σύστημα init στις μοντέρνες διανομές Linux, μπορείτε να χρησιμοποιήσετε αυτό για αυτό τον σκοπό.
Απλά βάλτε ένα αρχείο στο `/etc/systemd/system/git-daemon.service` με αυτά τα περιεχόμενα:

[source,console]

Description=Start Git Daemon

ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/

Restart=always RestartSec=500ms

StandardOutput=syslog StandardError=syslog SyslogIdentifier=git-daemon

User=git Group=git

WantedBy=multi-user.target

Ίσως προσέξατε ότι ο δαίμονας του Git daemon ξεκιά εδώ ως `git` τόσο για την ομάδα όσο και για τον χρήστη.
Τροποποιήστε το ώστε να το προσαρμόσετε στις ανάγκες σας και σιγουρευτείτε ότι ο χρήστης που δίνεται υπάρχει στο σύστημα.
Επίσης, ελέγξτε ότι το εκτελέσιμο αρχείο του Git βρίσκεται πράγματι στο `/usr/bin/git` αλλιώς αλλάξτε τη διαδρομή καταλλήλως.

Τέλος, θα εκτελέσετε `systemctl enable git-daemon` ώστε η υπηρεσία να ξεκινά αυτόματα κατά την εκκίνηση του υπολογιστή και μπορείτε να εκκινήσετε και να σταματήσετε την υπηρεσία με `systemctl start git-daemon` και `systemctl stop git-daemon` αντίστοιχα.

Σε άλλα συστήματα, ίσως θελήσετε να χρησιμοποιήσετε το `xinetd`, ένα script στο σύστημά σας `sysvinit` ή κάτι άλλο -- με την προϋπόθεση ότι μπορείτε δαιμονοποιήσετε αυτή την εντολή και να την παρακολουθείτε με κάποιον τρόπο.

Σε άλλα συστήματα, ίσως θελήσετε να χρησιμοποιήσετε το `xinetd`, ένα script στο σύστημά σας `sysvinit` ή κάτι άλλο -- εφόσον μπορείτε να δαιμονοποιήσετε αυτή την εντολή και να την παρακολουθείτε.

Στη συνέχεια, πρέπει να ενημερώσετε το Git ποια αποθετήρια επιτρέπουν την πρόσβαση σε διακομιστές Git χωρίς ταυτοποίηση.
Μπορείτε να το κάνετε σε κάθε αποθετήριο δημιουργώντας ένα αρχείο που ονομάζεται `git-daemon-export-ok`.

[source,console]

$ cd /path/to/project.git $ touch git-daemon-export-ok

Η παρουσία αυτού του αρχείου λέει στο Git ότι επιτρέπεται να εξυπηρετήσει αυτό το έργο χωρίς ταυτοποίηση.




=== Έξυπνο HTTP

(((serving repositories, HTTP)))
Έχετε πλέον ταυτοποιημένη πρόσβαση μέσω SSH και μη-ταυτοποιημένη πρόσβαση μέσω του `git://`, αλλά υπάρχει και ένα πρωτόκολλο που μπορεί να κάνει και τα δύο ταυτόχρονα.
Η εγκατάσταση του έξυπνου HTTP βασικά απλά ενεργοποιεί ένα script CGI, που παρέχεται με τον Git και ονομάζεται `git-http-backend`, στον διακομιστή. (((εντολές git, http-backend)))
Αυτό το CGI θα διαβάσει τη διαδρομή και τις κεφαλίδες που θα σταλούν με `git fetch` ή `git push` σε μια διεύθυνση URL HTTP και θα καθορίσει εάν ο πελάτης μπορεί να επικοινωνήσει μέσω HTTP (κάτι που ισχύει για όλους τους πελάτες από την έκδοση 1.6.6 και μετά).
Αν το CGI διαπιστώσει ότι ο πελάτης είναι έξυπνος, θα επικοινωνήσει μαζί του έξυπνα, αλλιώς θα επανέλθει στη χαζή συμπεριφορά (συνεπώς έχει προς-τα-πίσω συμβατότητα για ανάγνωσεις με τους παλαιότερους πελάτες).

Ας δούμε αναλυτικά μία πολύ βασική ρύθμιση.
Θα υποθέσουμε ότι ο διακομιστής CGI είναι ο Apache.
Αν δεν έχετε Apache, μπορείτε να το κάνετε σε ένα κουτί Linux με κάτι σαν αυτό: (((Apache)))

[source,console]

$ sudo apt-get install apache2 apache2-utils $ a2enmod cgi alias env

Αυτό ενεργοποιεί επίσης τις λειτουργικές μονάδες (modules) `mod_cgi`, `mod_alias` και `mod_env`, που είναι απαραίτητες για να λειτουργήσει σωστά όλο αυτό.

Θα πρέπει επίσης να ορίσετε την ομάδα χρηστών Unix φακέλων `/srv/git` σε `www-data`, ώστε ο web server να έχει πρόσβαση ανάγνωσης και εγγραφής στα αποθετήρια, διότι ο Apache που τρέχει το script CGA θα τρέχει εξ ορισμού ως ο χρήστης:

[source,console]

$ chgrp -R www-data /srv/git

Στη συνέχεια πρέπει να προσθέσουμε κάποια πράγματα στην παραμετροποίηση του Apache για να εκτελέσετε το `git-http-backend` ως τον handler για οτιδήποτε μπαίνει στη διαδρομή `/git` του web server σας.

[source,console]

SetEnv GIT_PROJECT_ROOT /srv/git SetEnv GIT_HTTP_EXPORT_ALL ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

Εάν παραλείψετε τη μεταβλητή περιβάλλοντος (environment variable) `GIT_HTTP_EXPORT_ALL`, τότε το Git θα εξυπηρετεί σε μη-ταυτοποιημένους πελάτες μόνο τα αποθετήρια που περιέχουν το αρχείο` `git-daemon-export-ok`, ακριβώς όπως έκανε και ο δαίμονας Git.

Στη συνέχεια θα πρέπει να πείτε στο Apache να επιτρέψει αιτήματα στο `git-http-backend` και να ταυτοποιεί με κάποιον τρόπο τις εγγραφές με ένα μπλοκ Auth σαν αυτό:

[source,console]

<Files "git-http-backend"> AuthType Basic AuthName "Git Access" AuthUserFile /srv/git/.htpasswd Require expr !(%{QUERY_STRING} -strmatch service=git-receive-pack || %{REQUEST_URI} =~ m#/git-receive-pack$#) Require valid-user </Files>

Αυτό προϋποθέτει την ύπαρξη ενος αρχείου `.htpasswd` που περιέχει τους κωδικούς πρόσβασης όλων των έγκυρων χρηστών.
Να ένα παράδειγμα προσθήκης του χρήστη "`schacon`" στο αρχείο :

[source,console]

$ htpasswd -c /srv/git/.htpasswd schacon

Τέλος, θα θελήσετε να κάνετε τις εγγραφές να πιστοποιούνται με κάποιον τρόπο, πιθανώς με ένα μπλοκ Auth όπως αυτό:

[source,console]

<LocationMatch "^/git/.*/git-receive-pack$"> AuthType Basic AuthName "Git Access" AuthUserFile /srv/git/.htpasswd Require valid-user </LocationMatch>

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

Δεν θέλουμε να μπούμε πολύ βαθιά στις ρυθμίσεις του Apache, καθώς ενδεχομένως χρησιμοποιείτε διαφορετικό διακομιστή ή έχετε διαφορετικές ανάγκες ταυτοποίησης.
Η βασική ιδέα είναι ότι το Git έρχεται με ένα CGI που ονομάζεται `git-http-backend` που όταν καλείται θα κάνει όλες τις διαπραγματεύσεις για αποστολή και λήψη δεδομένων μέσω HTTP.
Δεν υλοποιεί το ίδιο την ταυτοποίηση, αλλά αυτό μπορεί εύκολα να ελεγχθεί στο επίπεδο του web server που τον καλεί.
Μπορείτε να κάνετε τα παραπάνω με σχεδόν οποιοδήποτε web server με δυνατότητα CGI, οπότε χρησιμοποιήστε αυτόν που γνωρίζετε καλύτερα.

[NOTE]
====
Περισσότερες πληροφορίες σχετικά με την παραμετροποίηση της ταυτοποίησης στον Apache, υπάρχουν στην τεκμηρίωση του Apache στην http://httpd.apache.org/docs/current/howto/auth.html[^]
====



=== GitWeb

(((serving repositories, GitWeb)))(((GitWeb)))
Τώρα που έχετε βασική πρόσβαση ανάγνωσης/εγγραφής και μόνο-για-ανάγνωση στο έργο σας, ίσως θελήσετε να δημιουργήσετε ένα απλό οπτικοκοποιητή (visualizer) ιστού.
Το Git συνοδεύεται από ένα script CGI που ονομάζεται GitWeb και χρησιμοποιείται μερικές φορές για αυτό τον σκοπό.

[[rgitweb]]
.Η διεπαφή χρήστη του GitWeb.
image::images/git-instaweb.png[Η διεπαφή χρήστη του GitWeb.]

Αν θέλετε να δείτε πώς θα μοιάζει το GitWeb για το έργο σας, το Git παρέχει με μια εντολή που ενεργοποιεί ένα προσωρινό στιγμιότυπο (instance) αν έχετε έναν ελαφρύ διακομιστή στο σύστημά σας όπως τον `lighttpd` ή τον `webrick`.
Στα μηχανήματα Linux, ο `lighttpd` είναι συχνά εγκατεστημένος, οπότε ίσως μπορείτε να τον τρέξετε εκτελώντας `git instaweb` στον κατάλογο του έργου.
Εάν τρέχετε Mac, το Leopard έρχεται προεγκατεστημένο με Ruby, έτσι το πιθανότερο είνα να είναι διαθέσιμος ο `webrick`.
Για να ξεκινήσετε το `instaweb` με handler που δεν είναι lighttpd, μπορείτε να το εκτελέσετε με την επιλογή `--httpd`. (((εντολές git, instaweb)))

[source,console]

$ git instaweb --httpd=webrick [2009-02-21 10:02:21] INFO WEBrick 1.3.1

Αυτό εκκινεί έναν διακομιστή HTTPD στη θύρα 1234 και στη συνέχεια ξεκινά αυτόματα ένα πρόγραμμα περιήγησης που ανοίγει σε αυτή τη σελίδα.
Είναι τόσο εύκολο.
Όταν τελειώσετε και θέλετε να τερματίσετε τη λειτουργία του διακομιστή, μπορείτε να εκτελέσετε την ίδια εντολή με την επιλογή `--stop`:

[source,console]

$ git instaweb --httpd=webrick --stop

Εάν θέλετε να τρέχετε συνεχώς τη διεπαφή ιστού σε έναν διακομιστή για την ομάδα σας ή για ένα έργο ανοιχτού κώδικα που φιλοξενείτε, θα πρέπει να ρυθμίσετε το script CGI να προσφέρεται από τον συνηθισμένο web server σας.
Ορισμένες διανομές Linux έχουν ένα πακέτο `gitweb` που μπορείτε να εγκαταστήσετε με `apt` ή `yum`, επομένως ίσως είναι καλό να δοκιμάσετε αυτό πρώτα.
Θα σς δείξουμε τη χειροκίνητη εγκατάσταση του GitWeb πολύ συνοπτικά.
Πρώτα πρέπει να πάρετε τον πηγαίο κώδικα του Git, με τον οποίο έρχεται το GitWeb, και να δημιουργήσετε το προσαρμοσμένο script CGI:

[source,console]

$ git clone git://git.kernel.org/pub/scm/git/git.git $ cd git/ $ make GITWEB_PROJECTROOT="/opt/git" prefix=/usr gitweb SUBDIR gitweb SUBDIR ../ make[2]: ‘GIT-VERSION-FILE’ is up to date. GEN gitweb.cgi GEN static/gitweb.js $ sudo cp -Rf gitweb /var/www/

Παρατηρήστε ότι πρέπει να πείτε στην εντολή πού να βρει τα αποθετήρια Git με τη μεταβλητή `GITWEB_PROJECTROOT`.
Τώρα, πρέπει να κάνετε το Apache να χρησιμοποιεί CGI για αυτό το script και για αυτόν τον σκοπό μπορείτε να προσθέσετε ένα VirtualHost:

[source,console]

<VirtualHost *:80> ServerName gitserver DocumentRoot /var/www/gitweb <Directory /var/www/gitweb> Options ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch AllowOverride All order allow,deny Allow from all AddHandler cgi-script cgi DirectoryIndex gitweb.cgi </Directory> </VirtualHost>

Επαναλαμβάνουμε ότι το GitWeb μπορεί να εξυπηρετηθεί από οποιονδήποτε web server, CGI ή Perl· αν προτιμάτε να χρησιμοποιήσετε κάτι άλλο, δεν θα είναι δύσκολο να το εγκαταστήσετε.
Σε αυτό το σημείο, θα πρέπει να μπορείτε να επισκεφτείτε την `http://gitserver/` για να δείτε τα αποθετήριά σας online.



=== GitLab

(((serving repositories, GitLab)))(((GitLab)))
Το GitWeb είναι αρκετά απλοϊκό.
Αν ψάχνετε για έναν πιο σύγχρονο διακομιστή Git με πολλές λειτουργικότητες, υπάρχουν μερικές λύσεις ανοιχτού κώδικα που μπορείτε να εγκαταστήσετε αντ' αυτού.
Καθώς το GitLab είναι μία από τις πιο δημοφιλείς, θα δούμε την εγκατάσταση και χρήση του ως παράδειγμα.
Η εγκατάσταση του GitLab είναι λίγο πιο περίπλοκη από αυτή του GitWeb και πιθανότα απαιτεί περισσότερη συντήρηση, αλλά είναι μια επιλογή με πολύ περισσότερα χαρακτηριστικά.
Το GitLab είναι λίγο πιο πολύπλοκο από το GitWeb και ενδεχομένως απαιτεί περισσότερη συντήρηση, αλλά είναι μία πολύ πιο ολοκληρωμένη λύση όσον αφορά στα διαθέσιμες λειτουργικότητες.

==== Εγκατάσταση

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

Οι άλλες επιλογές εγκατάστασης είναι:

* Χάρτης GitLab Helm, για χρήση με το Kubernetes.
* Docker-οποιημένα πακέτα GitLab για χρήση με το Docker.
* Από πηγαίο κώδικα.
* Προμηθευτές cloud όπως οι AWS, Google Cloud Platform, Azure, OpenShift και Digital Ocean.

Για περισσότερες πληροφορίες διαβάστε το https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/README.md[αρχείο readme του GitLab Community Edition (CE)^].

==== Διαχείριση

Η διεπαφή διαχείρισης του GitLab είναι προσπελάσιμη μέσα από το web.
Απλά οδηγείτε το πρόγραμμα περιήγησής σας στο hostname ή τη διεύθυνση IP στην οποία είναι εγκατεστημένο το GitLab και συνδεθείτε ως χρήστης admin.
Το προεπιλεγμένο όνομα χρήστη είναι `admin@local.host` και ο προεπιλεγμένος κωδικός πρόσβασης είναι` 5iveL!Fe` (που πρέπει να αλλάξετε αμέσως).
Μόλις συνδεθείτε, κάνετε κλικ στο εικονίδιο "`Admin area`" στο επάνω δεξιά μενού.

[[rgitlab_menu]]
.Το εικονίδιο "`Admin area`" στο μενού του GitLab.
image::images/gitlab-menu.png[Το εικονίδιο “Admin area” στο μενού του GitLab.]

===== Χρήστες

Όλοι όσοι χρησιμοποιούν τον διακομιστή GitLab πρέπει να έχουν λογαριασμό χρήστη.
Οι λογαριασμοί χρηστών είναι πολύ απλοί· βασικά είναι μια συλλογή προσωπικών πληροφοριών προσαρτημένα σε δεδομένα σύνδεσης.
Κάθε λογαριασμός χρήστη συνοδεύεται από έναν *ονοματοχώρο* (namespace), το οποίο είναι μια λογική ομαδοποίηση έργων που ανήκουν σε αυτόν τον χρήστη.
Εάν ο χρήστης +jane+ είχε ένα έργο με όνομα +project+, η διεύθυνση URL του έργου θα ήταν `http://server/jane/project`.

[[rgitlab_users]]
.Η οθόνη διαχείρισης χρηστών του GitLab.
image::images/gitlab-users.png[Η οθόνη διαχείρισης χρηστών του GitLab.]

Μπορείτε να καταργήσετε ένα χρήστη με δύο τρόπους:
Αν "`μπλοκάρετε`" ενα χρήστη τον εμποδίζετε να συνδεθεί στο στιγμιότυπο (instance) του GitLab, αλλά όλα τα δεδομένα κάτω από τον ονοματοχώρο του χρήστη θα διατηρηθούν και οι υποβολές που έχει υπογράψει με τη διεύθυνση e-mail του χρήστη θα εξακολουθούν να είναι συνδεδεμένες στο προφίλ του.

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

[[r_gitlab_groups_section]]
===== Ομάδες

Μια ομάδα (group) στο GitLab είναι μια συλλογή έργων μαζί με δεδομένα σχετικά με τον τρόπο με τον οποίο οι χρήστες έχουν πρόσβαση σε αυτά τα έργα.
Κάθε ομάδα έχει ένα ονοματοχώρο έργου (ακριβώς όπως και οι χρήστες), οπότε αν η ομάδα +training+ έχει ένα έργο +materials+, η διεύθυνση URL του θα είναι http://server/training/materials[].

[[rgitlab_groups]]
.Οθόνη διαχείρισης ομάδων του GitLab.
image::images/gitlab-groups.png[Οθόνη διαχείρισης ομάδων του GitLab.]

Κάθε ομάδα συσχετίζεται με έναν αριθμό χρηστών, καθένας από τους οποίους έχει το δικό του επίπεδο δικαιωμάτων για τα έργα της ομάδας και την ίδια την ομάδα.
Αυτά ποικίλλουν από "`Guest`" (επισκέπτης) (έχουν πρόσβαση μόνον σε θέματα και chat) μέχρι "`Owner`" (κάτοχος) (έχει πλήρης έλεγχο της ομάδας, των μελών και των έργων της).
Οι τύποι δικαιωμάτων είναι πάρα πολλοί για να τους αναφέρουμε εδώ, αλλά το GitLab έχει έναν σχετικό χρήσιμο σύνδεσμο στην οθόνη διαχείρισης.

===== Έργα

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

Κάθε έργο έχει επίσης ένα επίπεδο ορατότητας, το οποίο ελέγχει ποιος έχει πρόσβαση στη σελίδα και το αποθετήριο αυτού του έργου.
Εάν ένα έργο είναι _Private_ (ιδιωτικό), ο κάτοχος του έργου πρέπει να παρέχει ρητά πρόσβαση σε συγκεκριμένους χρήστες.
Ένα _Internal_ (εσωτερικό) έργο είναι ορατό σε οποιονδήποτε συνδεδεμένο χρήστη και ένα _Public_ (δημόσιο) έργο είναι ορατό σε οποιονδήποτε.
Σημειώστε ότι το επίπεδο ορατότητας του έργου ελέγχει τόσο την πρόσβαση `git fetch`, όσο και την πρόσβαση στη διαδικτυακή διεπαφή χρήστη (web UI) για αυτό το έργο.

===== Άγκιστρα

Το GitLab περιλαμβάνει υποστήριξη για άγκιστρα (hooks), τόσο σε επίπεδο έργου όσο και σε επίπεδο συστήματος.
Και για τα δύο και κάθε φορά που προκύπτουν σχετικά γεγονότα, ο διακομιστής GitLab θα εκτελέσει ένα HTTP POST με κάποια περιγραφικά JSON.
Αυτός είναι ένας πολύ καλός τρόπος σύνδεσης των αποθετηρίων σας Git και του στιγμιότυπου GitLab με τον υπόλοιπο αυτοματισμό της ανάπτυξης του έργου, όπως οι διακομιστές CI, οι χώροι συνομιλίας (chat rooms) και τα εργαλεία ανάπτυξης.

==== Βασική χρήση

Το πρώτο πράγμα που θα θελήσετε να κάνετε με το GitLab είναι να δημιουργήσετε ένα νέο έργο.
Αυτό επιτυγχάνεται με κλικ στο εικονίδιο "`+`" στη γραμμή εργαλείων.
Θα σας ζητηθεί το όνομα του έργου, ο ονοματοχώρος στον οποίο θέλετε να ανήκει και το επίπεδο ορατότητάς του.
Τα περισσότερα στοιχεία από αυτά δεν είναι μόνιμα και μπορούν να επαναρυθμιστούν αργότερα στις ρυθμίσεις.
Κάνετε κλικ στο κουμπί "`Create Project`" και τελειώσατε.

Από τη στιγμή που το έργο αρχίζει να υπάρχει, το πιθανότερο είναι να θελήσετε να το συνδέσετε με ένα τοπικό αποθετήριο Git.
Κάθε έργο είναι προσβάσιμο μέσω HTTPS ή SSH, καθένα από τα οποία μπορεί να χρησιμοποιηθεί για να οριστούν οι ρυθμίσεις κατά τη διαμόρφωση ενός απομακρυσμένου Git.
Οι διευθύνσεις URL είναι ορατές στο επάνω μέρος της αρχικής σελίδας του έργου.
Αν έχετε ένα τοπικό αποθετήριο, με την παρακάτω εντολή θα δημιουργήσετε ένα απομακρυσμένο αποθετήριο με όνομα `gitlab` στη φιλοξενούμενη τοποθεσία:

[source,console]

$ git remote add gitlab https://server/namespace/project.git

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

[source,console]
Η διεπαφή χρήστη web παρέχει πρόσβαση σε πολλές χρήσιμες προβολές του ίδιου του αποθετηρίου.
Η αρχική σελίδα του κάθε έργου παρουσιάζει την πρόσφατη δραστηριότητα και οι συνδέσεις στην κορυφή θα σας οδηγήσουν σε προβολές των αρχείων του έργου και του αρχείου καταγραφής εργασιών.

==== Συνεργασίες

Ο απλούστερος τρόπος συνεργασίας σε ένα έργο GitLab είναι να δώσετε στον άλλο χρήστη δικαίωμα άμεσης ώθησης στο αποθετήριο Git.
Μπορείτε να προσθέσετε έναν χρήστη σε ένα έργο πηγαίνοντας στην ενότητα "`Members`" (Μέλη) των ρυθμίσεων του συγκεκριμένου έργου και δίνοντας στον νέο χρήστη ένα επίπεδο πρόσβασης (τα διάφορα επίπεδα πρόσβασης αναλύονται λίγο στην ενότητα <<r_gitlab_groups_section>>).
Παρέχοντας στον χρήστη επίπεδο πρόσβασης "`Developer`" (προγραμματιστή) ή ανώτερο, ο χρήστης μπορεί να ωθήσει υποβολές και κλάδους απευθείας στο αποθετήριο.

Ένας άλλος, πιο ελεγχόμενος τρόπος συνεργασίας είναι με τη χρήση αιτήσεων συγχώνευσης (merge requests).
Αυτή η δυνατότητα επιτρέπει σε κάθε χρήστη που μπορεί να δει ένα έργο να συνεισφέρει σε αυτό με ελεγχόμενο τρόπο.
Οι χρήστες με άμεση πρόσβαση μπορούν απλά να δημιουργήσουν έναν κλάδο, να ωθήσουν υποβολές σε αυτόν και να θέσουν ένα αίτημα συγχώνευσης από τον κλάδο τους στον κλάδο `master` ή σε οποιονδήποτε άλλο κλάδο.
Οι χρήστες που δεν έχουν δικαίωμα ώθησης σε ένα αποθετήριο μπορούν να το κάνουν αποσχίσουν (fork) (δηλ. να δημιουργήσουν το δικό τους αντίγραφο), να ωθούν υποβολές σε _αυτό_ το αντίγραφο και να κάνουν αίτημα συγχώνευσης από το αντίγραφό τους στο κύριο έργο.
Αυτό το μοντέλο επιτρέπει στον κάτοχο να έχει τον πλήρη έλεγχο του τι πηγαίνει στο αποθετήριο και πότε, καθώς επιτρέπει συνεισφορές και από μη αξιόπιστους χρήστες.

Τα αιτήματα συγχώνευσης και τα ζητήματα που προκύπτουν είναι τα κύρια θέματα μακροχρόνιων συζητήσεων στο GitLab.
Κάθε αίτηση συγχώνευσης επιτρέπει μια γραμμή-προς-γραμμή συζήτηση για την προτεινόμενη αλλαγή (η οποία υποστηρίζει ένα ελαφρύ είδος αναθεώρησης κώδικα) καθώς και ένα νήμα γενικής συζήτησης.
Και οι δύο μπορούν να ανατεθούν σε χρήστες ή να οργανωθούν σε ορόσημα (milestones).

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




=== Επιλογές φιλοξενίας από τρίτους

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

Σήμερα υπάρχει ένας τεράστιος αριθμός επιλογών φιλοξενίας από τους οποίους μπορείτε να διαλέξετε, η καθεμία με διαφορετικά πλεονεκτήματα και μειονεκτήματα.
Μπορείτε να βρείτε μια ενημερωμένη λίστα στη σελίδα GitHosting στο κύριο wiki Git στη διεύθυνση https://git.wiki.kernel.org/index.php/GitHosting[^].

Στο κεφάλαιο <<ch06-github>> θα καλύψουμε τη χρήση του GitHub, διότι αυτός είναι ο μεγαλύτερος διακομιστής Git και ούτως ή άλλως ίσως χρειαστεί να αλληλεπιδράσετε με τα έργα που φιλοξενούνται σε αυτόν, αλλά υπάρχουν και άλλοι δεκάδες διακομιστές Git από τους οποίους μπορείτε επιλέξετε αν δεν θέλετε να δημιουργήσετε τον δικό σας διακομιστή Git.


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

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

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

Θα πρέπει να είναι αρκετά απλό να προσδιορίσετε ποια λύση ή ποιος συνδυασμός λύσεων είναι κατάλληλα για σας και τον οργανισμό σας.



[#ch05-distributed-git]
[[r_distributed_git]]
== Κατανεμημένο Git

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

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

=== Κατανεμημένες ροές εργασίας

(((ροές εργασίας)))
Σε αντίθεση με τα Συγκεντρωτικά Συστήματα Ελέγχου Έκδοσεων (CVCS), η κατανεμημένη φύση του Git σάς επιτρέπει να είστε πολύ πιο ευέλικτοι όσον αφορά στη συνεργασία των προγραμματιστών στο πλαίσιο ενός έργου.
Στα συγκεντρωτικά συστήματα κάθε προγραμματιστής είναι ένας κόμβος που συνεργάζεται λίγο πολύ εξίσου με έναν κεντρικό κόμβο (hub).
Ωστόσο, στο Git, κάθε προγραμματιστής είναι δυνητικά τόσο κόμβος όσο και κεντρικό σημείο -- δηλαδή, κάθε προγραμματιστής μπορεί να συνεισφέρει κώδικα σε άλλα αποθετήρια και να διαχειρίζεται ένα δημόσιο αποθετήριο στο οποίο άλλοι μπορούν να βασίσουν τη δική τους εργασία και στο οποίο μπορούν να συνεισφέρουν.
Αυτό παρέχει ένα ευρύ φάσμα δυνατοτήτων όσον αφορά στις ροές εργασίας για το έργο σας ή/και την ομάδα σας· θα καλύψουμε μερικά συνηθισμένα μοντέλα που εκμεταλλεύονται αυτή την ευελιξία.
Θα αναλύσουμε τα δυνατά σημεία αλλά και τις ενδεχόμενες αδυναμίες κάθε μοντέλου· μπορείτε να επιλέξετε μόνον ένα από αυτά ή να δανειστείτε λειτουργικότητες από περισσότερα και να τις συνταιριάξετε.

==== Συγκεντρωτική ροή εργασίας

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

.Συγκεντρωτική ροή εργασίας.
image::images/centralized_workflow.png[Συγκεντρωτική ροή εργασίας.]

Αυτό σημαίνει ότι εάν δύο προγραμματιστές κλωνοποιήσουν από τον κεντρικό κόμβο και κάνουν αλλαγές και οι δύο, ο πρώτος προγραμματιστής που θα ωθήσει τις αλλαγές τους μπορεί να το κάνει χωρίς προβλήματα.
Ο δεύτερος προγραμματιστής πρέπει να συγχωνεύσει την εργασία του πρώτου πριν ωθήσει τις δικές του αλλαγές, ώστε να μην αντικαταστήσει τις αλλαγές του πρώτου προγραμματιστή.
Αυτό ισχύει ισχύει και στο Git όπως και στο Subversion (((Subversion))) (ή οποιοδήποτε CVCS) και αυτό το μοντέλο λειτουργεί απολύτως καλά και στο Git.

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

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

Αυτό δεν περιορίζεται μόνο στις μικρές ομάδες.
Με το μοντέλο διακλάδωσης του Git είναι δυνατό για εκατοντάδες προγραμματιστές να δουλέψουν με επιτυχία σε ένα μόνο έργο μέσω δεκάδων κλάδων ταυτόχρονα.

[[r_integration_manager]]
==== Ροή εργασίας με διαχειριστή ενσωμάτωσης (integration manager)

(((ροές εργασίας, διαχειριστής ενσωμάτωσης)))
Επειδή το Git σάς επιτρέπει να έχετε πολλαπλά απομακρυσμένα αποθετήρια, είναι δυνατό να έχετε μια ροή εργασίας στην οποία κάθε προγραμματιστής έχει δικαίωμα εγγραφής στο δικό του δημόσιο αποθετήριο και δικαίωμα ανάγνωσης σε όλα τα άλλα αποθετήρια.
Αυτό το σενάριο περιλαμβάνει συχνά ένα κανονικό (canonical) αποθετήριο που αντιπροσωπεύει το "`επίσημο`" έργο.
Για να συνεισφέρετε σε αυτό το έργο, δημιουργείτε τον δικό σας δημόσιο κλώνο του έργου και ωθείτε τις αλλαγές σας σε αυτόν.
Στη συνέχεια, μπορείτε να στείλετε ένα αίτημα στον διαχειριστή του κύριου έργου να τραβήξει τις αλλαγές σας.
Τότε ο διαχειριστής μπορεί να προσθέσει το αποθετήριό σας ως απομακρυσμένο, να δοκιμάσει τις αλλαγές σας τοπικά, να τις συγχωνεύσει στον κλάδο του και να τις ωθήσει στο αποθετήριό του.
Η διαδικασία λειτουργεί ως εξής (βλ. <<rwfdiag_b>>):

1. Ο διαχειριστής του έργου ωθεί στο δημόσιο αποθετήριό του.
2. Ένας συνεργάτης κλωνοποιεί αυτό το αποθετήριο και κάνει αλλαγές.
3. Ο συνεργάτης ωθεί στο δικό του δημόσιο αντίγραφο.
4. Ο συνεργάτης αποστέλλει στον διαχειριστή ένα e-mail ζητώντας του να κάνει αλλαγές.
5. Ο διαχειριστής προσθέτει το αποθετήριο του συνεργάτη ως απομακρυσμένο και συγχωνεύει τοπικά.
6. Ο διαχειριστής ωθεί τις συγχωνευμένες αλλαγές στο κύριο αποθετήριο.

[[rwfdiag_b]]
.Ροή εργασίας με διαχειριστη ενσωμάτωσης.
image::images/integration-manager.png[Ροή εργασίας με διαχειριστη ενσωμάτωσης.]

(((απόσχιση)))
Αυτή είναι μια πολύ συνηθισμένη ροή εργασίας με εργαλεία όπως το GitHub ή το GitLab, που βασίζονται σε κεντρικούς κόμβους και στην οποία είναι εύκολο να κλωνοποιήσετε ένα έργο και να ωθήσετε τις αλλαγές σας στον δικό σας κλώνο, όπου μπορούν να τις δουν όλοι.
Ένα από τα κύρια πλεονεκτήματα αυτής της προσέγγισης είναι ότι μπορείτε να συνεχίσετε να εργάζεστε και ο διαχειριστής του κύριου αποθετηρίου μπορεί να τραβήξει τις αλλαγές σας όποτε θέλει.
Οι συνεργάτες δεν χρειάζεται να περιμένουν το έργο να ενσωματώσει τις αλλαγές τους -- ο καθένας μπορεί να εργαστεί με τον δικό του ρυθμό.

==== Ροή εργασίας δικτάτορα και υπαρχηγών

(((ροές εργασίας, δικτάτορας και υπαρχηγοί)))
Αυτή είναι μια παραλλαγή της ροής εργασίας πολλαπλών αποθετηρίων.
Χρησιμοποιείται γενικά από τεράστια έργα με εκατοντάδες συνεργάτες· ένα διάσημο παράδειγμα είναι ο πυρήνας του Linux.
Διάφοροι διαχειριστές ενσωμάτωσης είναι υπεύθυνοι για ορισμένα τμήματα του αποθετηρίου· αυτοί ονομάζονται υπαρχηγοί.
Όλοι οι υπαρχηγοί έχουν έναν διευθυντή ενσωμάτωσης γνωστό και ως καλόβουλο δικτάτορα.
Το αποθετήριο του καλόβουλου δικτάτορα χρησιμεύει ως αποθετήριο αναφοράς από το οποίο όλοι οι συνεργάτες τραβούν αρχεία.
Η διαδικασία λειτουργεί ως εξής (βλ. <<rwfdiag_c>>):

1. Οι απλοί προγραμματιστές ασχολούνται με τον θεματικό κλάδο τους και επανατοποθετούν (rebase) την εργασία τους στον κλάδο `master`.
   Ο κλάδος `master` ανήκει στο αποθετήριο αναφοράς στο οποίο ωθεί ο δικτάτορας.
2. Οι υπαρχηγοί συγχωνεύουν τους κλάδους των προγραμματιστών ο καθένας στον δικό του στον κλάδο `master`.
3. Ο δικτάτορας συγχωνεύει τους κλάδους `master` των υπαρχηγών στον κλάδο `master` του δικτάτορα.
4. Ο δικτάτορας ωθεί τον δικό του κλάδο `master` στο αποθετήριο αναφοράς, έτσι ώστε οι άλλοι προγραμματιστές να μπορέσουν να επανατοποθετηθούν σε αυτόν.

[[rwfdiag_c]]
.Ροή εργασίας με καλόβουλο δικτάτορα.
image::images/benevolent-dictator.png[Ροή εργασίας με καλόβουλο δικτάτορα.]

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

==== Περίληψη ροών εργασίας

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



[[r_contributing_project]]
=== Συνεισφέροντας σε ένα έργο

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

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

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

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

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

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

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

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

.Έξοδος μίας `git diff --check`.
image::images/git-diff-check.png[Έξοδος μίας `git diff --check`.]

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

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

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

Το τελευταίο πράγμα που πρέπει να έχετε υπόψη είναι το μήνυμα υποβολής.
Η δημιουργία μηνυμάτων υψηλής ποιότητας διευκολύνει τη χρήση του Git και τη συνεργασία.
Έχετε ως γενικό κανόνα τα μηνύματά σας να ξεκινούν με μία μόνο γραμμή που δεν υπερβαίνει τους 50 χαρακτήρες και περιγράφει συνοπτικά το σύνολο αλλαγών, να ακολουθεί μια κενή γραμμή και στη συνέχεια να ακολουθεί μια πιο λεπτομερής εξήγηση.
Το έργο Git απαιτεί η λεπτομερέστερη εξήγηση να περιλαμβάνει το κίνητρό σας για την αλλαγή και να αντιπαραβάλλετε την εφαρμογή της με την προηγούμενη συμπεριφορά -- αυτή είναι μια καλή κατευθυντήρια γραμμή που πρέπει να ακολουθείτε.
Επίσης είναι καλή ιδέα να χρησιμοποιείτε την προστακτική έγκλιση σε αυτά τα μηνύματα:
Αντί για "`Διόρθωσα ένα bug`" ή "`Διόρθωση bug`", χρησιμοποιείτε "`Διόρθωσε bug`".
Ακολουθεί ένα πρότυπο που μπορείτε να ακολουθείτε και το οποίο έχουμε τροποποιήσει ελαφρά από αυτό που https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[γράφτηκε από τον Tim Pope^]:

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

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

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

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

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

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

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

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


[NOTE]
.Κάντε ό,τι λέμε, όχι ό,τι κάνουμε.
====
Για χάρη συντομίας, For the sake of brevity, many of the examples in this book don't have nicely-formatted commit messages like this; instead, we simply use the `-m` option to `git commit`.

In short, do as we say, not as we do.
====
πολλά από τα παραδείγματα αυτού του βιβλίου δεν έχουν μηνύματα υποβολών που είναι μορφοποιημένα όμορφα, όπως παραπάνω· αντί γι' αυτό χρησιμοποιούμε την επιλογή `-m` στην εντολή `git commit`.

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

[[r_private_team]]
==== Ιδιωτικές μικρές ομάδες

(((contributing, private small team)))
Η απλούστερη ρύθμιση που ενδεχομένως θα συναντήσετε είναι ένα ιδιωτικό έργο με έναν ή δύο ακόμα προγραμματιστές.
"`Ιδιωτικό`" εδώ σημαίνει κλειστού-κώδικα -- που δεν είναι προσβάσιμος από τον έξω κόσμο.
Εσείς και όλοι οι άλλοι προγραμματιστές έχετε πρόσβαση ώθησης στο αποθετήριο.

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

[source,console]
-----
# Μηχάνημα του Τζον
$ 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(-)
-----

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

[source,console]
-----
# Μηχάνημα της Τζέσικα
$ 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(-)
-----

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

[source,console]
-----
# Μηχάνημα της Τζέσικα
$ git push origin master
...
To jessica@githost:simplegit.git
   1edee6b..fbff5bc  master -> master
-----

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

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

[source,console]
-----
# Μηχάνημα του Τζον
$ 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) τις αλλαγές της Τζέσικα και να τις συγχωνεύσει στο τοπικό του αποθετήριο προτού του επιτραπεί να ωθήσει:

Το πρώτο βήμα που κάνει ο Τζον είνα να ανακτήσει τη δουλειά της Τζέσικα (αυτό μόνο _ανακτά_ τη δουλειά που ανέβασε η Τζέσικα, δεν τη συγχωνεύει ακόμα στη δουλειά του Τζον):
[source,console]
-----
$ git fetch origin
...
From john@githost:simplegit
 + 049d078...fbff5bc master     -> origin/master
-----

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

.Το αποκλίνον ιστορικό του Τζον.
image::images/small-team-1.png[Το αποκλίνον ιστορικό του Τζον]

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

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

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

.Το αποθετήριο του Τζον μετά τη συγχώνευση του `origin/master`.
image::images/small-team-2.png[Το αποθετήριο του Τζον μετά τη συγχώνευση του `origin/master`.]

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

[source,console]
-----
$ git push origin master
...
To john@githost:simplegit.git
   fbff5bc..72bbc59  master -> master
-----

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

.Ιστορικό του Τζον μετά την ώθηση στον διακομιστή `origin`.
image::images/small-team-3.png[Ιστορικό του Τζον μετά την ώθηση στον διακομιστή `origin`.]

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

.Θεματικός κλάδος της Τζέσικα.
image::images/small-team-4.png[Θεματικός κλάδος της Τζέσικα.]

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

[source,console]
-----
# Μηχάνημα της Τζέσικα
$ git fetch origin
...
From jessica@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master
-----

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

.Ιστορικό της Τζέσικα μετά την ανάκτηση των αλλαγών του Τζον.
image::images/small-team-5.png[Ιστορικό της Τζέσικα μετά την ανάκτηση των αλλαγών του Τζον.]

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

[source,console]
-----
$ 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`").
Θα εξετάσουμε λεπτομερώς σε αυτή τη σύνταξη στην ενότητα <<r_commit_ranges>>.

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

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

[source,console]
-----
$ 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`:

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

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

[source,console]
-----
$ 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(-)
-----

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

.Το ιστορικό της Τζέσικα μετά τη συγχώνευση των αλλαγών του Τζον.
image::images/small-team-6.png[Το ιστορικό της Τζέσικα μετά τη συγχώνευση των αλλαγών του Τζον.]

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

[source,console]
-----
$ git push origin master
...
To jessica@githost:simplegit.git
   72bbc59..8059c15  master -> master
-----

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

.Το ιστορικό της Τζέσικα μετά την ώθηση όλων των αλλαγών στον διακομιστή.
image::images/small-team-7.png[Το ιστορικό της Τζέσικα μετά την ώθηση όλων των αλλαγών στον διακομιστή.]

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

.Γενική ακολουθία γεγονότων για μια απλή ροή εργασίας με πολλούς προγραμματιστές.
image::images/small-team-flow.png[Γενική ακολουθία γεγονότων για μια απλή ροή εργασίας με πολλούς προγραμματιστές.]

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

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

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

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

[source,console]
-----
# Μηχάνημα της Τζέσικα
$ 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` -- μόνον οι μηχανικοί ενσωμέτωσης έχουν -- γι' αυτό πρέπει να ωθήσει σε έναν άλλο κλάδο για να συνεργαστεί με τον Τζον:

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

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

[source,console]
-----
# Μηχάνημα της Τζέσικα
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'
-----

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

[source,console]
-----
$ 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(-)
-----

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

.Αρχικό ιστορικό υποβολών της Τζέσικα.
image::images/managed-team-1.png[Αρχικό ιστορικό υποβολών της Τζέσικα.]

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

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

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

[source,console]
-----
$ 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`, η Τζέσικα θέλει να ωθήσει σε _αυτόν_ τον κλάδο, το οποίο και κάνει με την εντολή:

[source,console]
-----
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee
-----

Αυτό ονομάζεται _refspec_.
Μια πιο  λεπτομερής συζήτηση για τα refspec του Git και διάφορα πράγματα που μπορείτε να κάνετε με αυτά υπάρχουν στην ενότητα <<r_refspec>>.
Παρατηρήστε επίσης τη σημαία `-u`· είναι συντομογραφία του `--set-upstream`, που ρυθμίζει τους κλάδους για ευκολότερη ώθηση και ελκυσμό αργότερα.

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

[source,console]
-----
$ git fetch origin
...
From jessica@githost:simplegit
   3300904..aad881d  featureA   -> origin/featureA
-----

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

[source,console]
-----
$ 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`:

[source,console]
-----
$ 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` και να ωθήσει το τελικό αποτέλεσμα στον διακομιστή:
[source,console]
-----
$ 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
-----

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

.Ιστορικό της Τζέσικα μετά την υποβολή σε έναν θεματικό κλάδο.
image::images/managed-team-2.png[Ιστορικό της Τζέσικα μετά την υποβολή σε έναν θεματικό κλάδο.]

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

.Ιστορικό της Τζέσικα μετά τη συγχώνευση και των δύο θεματικών κλάδων της.
image::images/managed-team-3.png[Ιστορικό της Τζέσικα μετά τη συγχώνευση και των δύο θεματικών κλάδων της.]

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

.Βασική ακολουθία αυτής της ροής εργασίας διαχειριζόμενη ομάδα.
image::images/managed-team-flow.png[Βασική ακολουθία αυτής της ροής εργασίας διαχειριζόμενη ομάδα.]

[[r_public_project]]
==== Αποσχισμένα δημόσια έργα

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

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

[source,console]
-----
$ git clone <url>
$ cd project
$ git checkout -b featureA
  ... work ...
$ git commit
  ... work ...
$ git commit
-----

[NOTE]
====
Ίσως θελήσετε να χρησιμοποιήσετε την εντολή `rebase -i` για να συνθλίψετε (squash) την εργασία σας σε μία μόνο υποβολή ή να αναδιατάξετε την εργασία σας στις υποβολές για να διευκολύνετε τον διαχειριστή που θα ελέγξει το επίθεμά σας -- βλ. <<r_rewriting_history>> για περισσότερες πληροφορίες σχετικά με τη διαδραστική αλλαγή βάσης.
====

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

[source,console]
-----
$ git remote add myfork <url>
-----

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

[source,console]
-----
$ git push -u myfork featureA
-----

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

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

[source,console]
-----
$ 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` του αποθετηρίου:

[source,console]
-----
$ 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`.
image::images/public-small-1.png[Αρχικό ιστορικό υποβολών με τη δουλειά από το `featureB`.]

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

[source,console]
-----
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
-----

Αυτό επανεγγράφει το ιστορικό σας, ώστε τώρα μοιάζει με το <<rpsp_b>>.

[[rpsp_b]]
.Ιστορικό υποβολών μετά από δουλειά στο `featureA`.
image::images/public-small-2.png[Ιστορικό υποβολών μετά από δουλειά στο `featureA`.]

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

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

(((εντολές git, merge, squash)))
[source,console]
-----
$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
  ... change implementation ...
$ git commit
$ git push myfork featureBv2
-----

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

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

.Ιστορικό υποβολών μετά τη δουλειά στο `featureBv2`.
image::images/public-small-3.png[Ιστορικό υποβολών μετά τη δουλειά στο `featureBv2`.]

[[r_project_over_email]]
==== Δημόσιο έργο μέσω e-mail

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

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

[source,console]
-----
$ git checkout -b topicA
  ... work ...
$ git commit
  ... work ...
$ git commit
-----

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

[source,console]
-----
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
-----

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

[source,console]
-----
$ 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.

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

[source,ini]
-----
[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = p4ssw0rd
  port = 993
  sslverify = false
-----

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

[source,console]
-----
$ 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`:

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

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

[source,console]
-----
$ 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 που μοιάζουν με κάτι σαν το παρακάτω για κάθε επίθεμα που στέλνετε:

[source,text]
-----
(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, επισκεφτείτε το  https://git-send-email.io[git-send-email.io^].
====

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

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

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



=== Συντήρηση ενός έργου

(((maintaining a project)))
Εκτός από το να γνωρίζετε πώς να συμβάλλετε αποτελεσματικά σε ένα έργο, πιθανότατα θα χρειαστεί να ξέρετε πώς να διαχειρίζεστε ένα αποθετήριο.
Αυτό μπορεί να συνίσταται στην αποδοχή και εφαρμογή των επιθεμάτων που δημιουργούνται με το `format-patch` και στέλνονται με e-mail σε εσάς ή στην ενσωμάτωση αλλαγών σε απομακρυσμένους κλάδους για αποθετήρια που έχετε προσθέσει ως απομακρυσμένα στο έργο σας.
Είτε διαχειρίζεστε ένα τυπικό αποθετήριο είτε θέλετε να βοηθήσετε επαληθεύοντας ή εγκρίνοντας επιθέματα, πρέπει να ξέρετε πώς να δέχεστε την εργασία με τρόπο που είναι κατά το δυνατό ξεκάθαρος στους άλλους συνεργάτες και βιώσιμος από εσάς μακροπρόθεσμα.

==== Εργασία σε θεματικούς κλάδους

(((κλάδοι, θεματικοί)))
Όταν σκέφτεστε να ενσωματώσετε νέα δουλειά, είναι γενικά καλή ιδέα να το δοκιμάσετε σε έναν _θεματικό κλάδο_ -- έναν προσωρινό κλάδο ειδικά σχεδιασμένο για να δοκιμάσετε αυτή την εργασία.
Με αυτόν τον τρόπο, είναι εύκολο να τροποποιήσετε ένα επίθεμα ξεχωριστά και να το παρατήσετε αν δεν λειτουργεί μέχρι να έχετε χρόνο να ξανασχοληθείτε μαζί του.
Αν δημιουργήσετε ένα απλό όνομα κλάδου με βάση το θέμα της εργασίας που πρόκειται να δοκιμάσετε, όπως `ruby_client` ή κάτι παρόμοια περιγραφικό, μπορείτε εύκολα να το θυμάστε, ακόμα κι αν χρειαστεί να το εγκαταλείψετε για αρκετό καιρό και να επιστρέψετε σε αυτό αργότερα.
Οι διαχειριστές έργων Git συνηθίζουν επίσης να οργανώνουν αυτούς τους κλάδους σε ονοματοχώρους (namespaces) -- όπως `sc/ruby_client`, όπου το `sc` είναι συντομογραφία για τον προγραμματιστή που  συνεισέφερε την εργασία.
Όπως ίσως θυμάστε, μπορείτε να δημιουργήσετε τον κλάδο να διακλαδίζεται από τον κύριο κλάδο σας ως εξής:

[source,console]
-----
$ git branch sc/ruby_client master
-----

Εναλλακτικά, αν θέλετε να μεταβείτε σε αυτόν αμέσως, μπορείτε να χρησιμοποιήσετε την επιλογή `-b`:

[source,console]
-----
$ git checkout -b sc/ruby_client master
-----

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

[[r_patches_from_email]]
==== Εφαρμογή επιθεμάτων από e-mail

(((e-mail, επιλογή επιθεμάτων από)))
Αν λάβετε επίθεμα μέσω e-mail και πρέπει να το ενσωματώσετε στο έργο σας, πρέπει να το εφαρμόσετε στον θεματικό κλάδο για να το αξιολογήσετε.
Υπάρχουν δύο τρόποι για να εφαρμόσετε ένα επίθεμα που λάβατε με e-mail: με `git apply` ή με `git am`.

===== Εφαρμογή επιθέματος με `git apply`

(((εντολές git, apply)))
Εάν λάβατε το επίθεμα από κάποιον που το δημιούργησε με την εντολή `git diff` ή `diff` του Unix (κάτι που δεν συνιστάται, όπως δείτε στην επόμενη ενότητα), μπορείτε να το εφαρμόσετε με την εντολή `git apply`.
Αν υποθέσουμε ότι έχετε αποθηκεύσει το επίθεμα στον φάκελο `/tmp/patch-ruby-client.patch`, μπορείτε να το εφαρμόσετε ως εξής:

[source,console]
-----
$ git apply /tmp/patch-ruby-client.patch
-----

Αυτή η εντολή τροποποιεί τα αρχεία στον κατάλογο εργασίας σας.
Είναι σχεδόν πανομοιότυπη με την εκτέλεση της εντολής `patch -p1` για την εφαρμογή του επιθέματος, αν και είναι πιο παρανοϊκή και δέχεται λιγότερες ασαφείς αντιστοιχίσεις από ότι η `patch`.
Επίσης, διαχειρίζεται προσθήκες, διαγραφές και μετονομασίες αρχείων εφόσον περιγράφονται στη μορφή `git diff`, κάτι που δεν κάνει η `patch`.
Τέλος, η `git apply` είναι ένα μοντέλο "`όλα ή τίποτα`" ("`apply all or abort all`") όπου είτε όλες οι αλλαγές εφαρμόζονται είτε καμία, ενώ η `patch` μπορεί να εφαρμόσει μερικώς επιθέματα αφήνοντας τον κατάλογο εργασίας σας σε μία περίεργη κατάσταση.
Η `git apply` είναι γενικά πολύ πιο συντηρητική από την `patch`.
Δεν θα δημιουργήσει αυτόματα κάποια υποβολή για σας -- μετά την εκτέλεσή της, θα πρέπει να βάλετε τις αλλαγές στον προθάλαμο και να τις υποβάλετε οι ίδιοι.

Μπορείτε επίσης να χρησιμοποιήσετε την `git apply` για να διαπιστώσετε αν επίθεμα εφαρμόζεται καθαρά πριν δοκιμάσετε την εφαρμογή του -- μπορείτε να εκτελέσετε το `git apply --check` με το επίθεμα:

[source,console]
-----
$ git apply --check 0001-see-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
-----

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

[[r_git_am]]
===== Εφαρμογή επιθέματος με την `git am`

(((εντολές git, am)))
Εάν ο συνεισφέρων είναι χρήστης του Git και ήταν αρκετά καλός ώστε να χρησιμοποιήσει την εντολή `format-patch` για να δημιουργήσει το επίθεμα, τότε η εργασία σας είναι ευκολότερη διότι το επίθεμα περιέχει πληροφορίες για τον συγγραφέα και ένα μήνυμα υποβολής για εσάς.
Αν είναι δυνατό, καλό είναι να ενθαρρύνετε τους συνεργάτες σας να χρησιμοποιούν την `format-patch` αντί για την `diff` για να δημιουργούν επιθέματα για σας.
Θα πρέπει να χρησιμοποιείτε την `git apply` μόνον για επιθέματα παλιού τύπου (legacy).

Για να εφαρμόσετε ένα επίθεμα που δημιουργείται με την `format-patch`, χρησιμοποιείτε την `git am`.
Από τεχνικής άποψης, η `git am` έχει φτιαχτεί για να διαβάζει ένα αρχείο mbox, που είναι μία απλή μορφή αρχείου κειμένου για την αποθήκευση ενός ή περισσοτέρων μηνυμάτων e-mail σε ένα αρχείο κειμένου.
Μοιάζει με κάτι τέτοιο:

[source,console]
-----
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
-----

Αυτή είναι η αρχή της εξόδου της εντολής `format-patch` που είδαμε στην προηγούμενη ενότητα.
Είναι επίσης μια έγκυρη μορφή ηλεκτρονικού μηνύματος mbox.
Εάν κάποιος σας έχει στείλει ηλεκτρονικά το επίθεμα χρησιμοποιώντας την `git send-email` και το κατεβάσετε σε μορφή mbox, τότε μπορείτε να κατευθύνετε την `git am` στο αρχείο mbox και αυτό θα αρχίσει να εφαρμόζει όλα τα επιθέματα που βλέπει.
Εάν τρέχετε ένα πρόγραμμα-πελάτη e-mail που μπορεί να αποθηκεύσει πολλά μηνύματα e-mail σε μορφή mbox, μπορείτε να αποθηκεύσετε ολόκληρες σειρές επιθεμάτων σε ένα αρχείο και στη συνέχεια να χρησιμοποιήσετε την `git am` για να εφαρμόσετε το καθένα ξεχωριστά.

Ωστόσο, αν κάποιος χρήστης ανέβασε ένα επίθεμα που δημιουργήθηκε με την `format-patch` σε ένα σύστημα εισιτηρίων ή κάτι παρόμοιο, μπορείτε να αποθηκεύσετε το αρχείο τοπικά και να περάσετε το αρχείο που αποθηκεύσατε στην `git am` για να το εφαρμόσετε:

[source,console]
-----
$ git am 0001-limit-log-function.patch
Applying: Add limit to log function
-----

Μπορείτε να δείτε ότι το επίθεμα εφαρμόστηκε καθαρά και η νέα υποβολή δημιουργήθηκε για σας αυτόματα.
Οι πληροφορίες του συγγραφέα λαμβάνονται από τις κεφαλίδες `From:` και `Date:` του e-mail και το μήνυμα της υποβολής λαμβάνεται από το `Subject:` και το σώμα (πριν από το επίθεμα) του μηνύματος.
Για παράδειγμα, εάν αυτή η ενημερωμένη έκδοση κώδικα εφαρμόστηκε από το παραπάνω παράδειγμα mbox, η υποβολή που θα δημιουργούνταν θα φαινόταν κάπως έτσι:

-----
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   Add limit to log function

   Limit log functionality to the first 20
-----

Οι πληροφορίες στο πεδίο `Commit` υποδεικνύουν το άτομο που εφάρμοσε το επίθεμα και την ώρα που έγινε η εφαρμογή αυτή.
Οι πληροφορίες στο πεδίο `Author` υποδεικνύουν το άτομο που αρχικά δημιούργησε το επίθεμα και πότε δημιουργήθηκε για πρώτη φορά.

Αλλά είναι πιθανό ότι το επίθεμα δεν θα εφαρμοστεί καθαρά.
Ίσως ο κύριος κλάδος σας έχει αποκλίνει πολύ από τον κλάδο από τον οποίο είχε διακλαδωθεί αυτό το επίθεμα ή το επίθεμα εξαρτάται από ένα άλλο επίθεμα που δεν έχετε εφαρμόσει ακόμα.
Σε αυτή την περίπτωση, η διαδικασία με την `git am` θα αποτύχει και θα σας ρωτήσει τι θέλετε να κάνετε:

[source,console]
-----
$ git am 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
-----

Αυτή η εντολή τοποθετεί σημάνσεις σύγκρουσης σε όλα τα αρχεία με τα οποία αντιμετωπίζει προβλήματα, όπως μια σύγκρουση συγχώνευσης ή αλλαγής βάσης.
Μπορείτε να επιλύσετε αυτό το πρόβλημα λίγο-πολύ με τον ίδιο τρόπο -- επεξεργάζεστε το αρχείο για να επιλύσετε τη σύγκρουση, τοποθετείτε το νέο αρχείο στον προθάλαμο και στη συνέχεια εκτελείτε την εντολή `git am --resolved` για να συνεχίσετε στο επόμενο επίθεμα:

[source,console]
-----
$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: See if this helps the gem
-----

Εάν θέλετε το Git να δοκιμάσει να επιλύσει τη σύγκρουση με λίγο πιο έξυπνο τρόπο, μπορείτε να περάσετε την επιλογή `-3` στην `git am`, η οποία θα κάνει το Git να επιχειρήσει μια τριμερή συγχώνευση.
Αυτή η επιλογή δεν είναι ενεργοποιημένη εξ ορισμού, επειδή δεν λειτουργεί εφόσον η υποβολή στην οποία λέει το επίθεμα ότι βασίζεται δεν βρίσκεται στο αποθετήριό σας.
Αν έχετε αυτή την υποβολή -- για παράδειγμα αν το επίθεμα βασίστηκε σε δημόσια υποβολή -- τότε η επιλογή `-3` είναι γενικά πολύ πιο έξυπνη όσον αφορά στην εφαρμογή ενός συγκρουόμενου επιθέματος λογισμικού:

[source,console]
-----
$ git am -3 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
-----

Σε αυτή την περίπτωση, χωρίς την επιλογή `-3`, το επίθεμα θα θεωρούνταν σύγκρουση.
Αφού χρησιμοποιήθηκε η επιλογή `-3`, το επίθεμα εφαρμόστηκε καθαρά.

Αν εφαρμόζετε μια σειρά από επιθέματα από ένα mbox, μπορείτε επίσης να εκτελέσετε την εντολή `am` σε διαδραστική (interactive) λειτουργία, η οποία σταματά σε κάθε επίθεμα που βρίσκει και ρωτά αν θέλετε να το εφαρμόσετε:

[source,console]
-----
$ git am -3 -i mbox
Commit Body is:
--------------------------
See if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
-----

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

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

[[r_checking_out_remotes]]
==== Checkοut απομακρυσμένων κλάδων

(((branches, remote)))
Εάν η συνεισφορά προήλθε από έναν χρήστη Git που δημιούργησε το δικό του αποθετήριο, ώθησε μερικές αλλαγές σε αυτό και έπειτα σάς έστειλε τη διεύθυνση URL του αποθετηρίου και το όνομα του απομακρυσμένου κλάδου στον οποίο έγιναν οι αλλαγές, μπορείτε να το προσθέσετε ως απομακρυσμένο και να κάνετε συγχωνεύσεις τοπικά.

Για παράδειγμα, εάν η Τζέσικα σας στείλει ένα e-mail που λέει ότι έχει μία εξαιρετική νέα λειτουργικότητα στον κλάδο `ruby-client` του αποθετηρίου της, μπορείτε να τη δοκιμάσετε προσθέτοντας το αποθετήριο ως απομακρυσμένο και κάνοντας check out τον κλάδο τοπικά:

[source,console]
-----
$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client
-----

Εάν η Τζέσικα ξαναστείλει e-mail αργότερα με κάποιον άλλο κλάδο που περιέχει άλλο ένα εξαιρετικό  χαρακτηριστικό, μπορείτε να κάνετε απευθείας `fetch` και `checkout` τοπικά διότι έχετε ήδη το απομακρυσμένο αποθετήριο εγκατεστημένο.

Αυτό είναι ιδιαίτερα χρήσιμο αν συνεργάζεστε με ένα άτομο με συχνά.
Εάν κάποιος συνεισφέρει ένα επίθεμα μόνο μία στις τόσες, τότε η αποδοχή του μέσω e-mail είναι ενδεχομένως λιγότερο χρονοβόρα από το να πρέπει όλοι να τρέχουν το δικό τους διακομιστή και να προσθαφαιρούν απομακρυσμένους διακομιστές συνεχώς για να πάρουν μιά χούφτα επιθέματα.
Επίσης, είναι μάλλον απίθανο να θέλετε να έχετε εκατοντάδες απομακρυσμένους διακομιστές, έναν για καθένα που συνεισφέρει καναδυό επιθέματα μια στις τόσες.
Ωστόσο, script και φιλοξενούμενες υπηρεσίες μπορεί να σας διευκολύνουν -- αυτό εξαρτάται σε μεγάλο βαθμό από τον τρόπο με τον οποίο εσείς και οι συνεισφέροντες αναπτύσσετε τον κώδικά σας.

Το άλλο πλεονέκτημα αυτής της προσέγγισης είναι ότι έχετε και το ιστορικό των υποβολών.
Παρόλο που ίσως έχετε ζητήματα συγχώνευσης, γνωρίζετε πού βρίσκεται η σχετική εργασία τους στο ιστορικό σας· μια κατάλληλη τριμερής συγχώνευση είναι η προτιμότερη επιλογή από τη χρήση της επιλογής `-3` με την ελπίδα ότι το επίθεμα δημιουργήθηκε από μια δημόσια υποβολή στην οποία έχετε πρόσβαση.

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

[source,console]
-----
$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
 * branch            HEAD       -> FETCH_HEAD
Merge made by the 'recursive' strategy.
-----

[[r_what_is_introduced]]
==== Προσδιορισμός του τι έχει εισαχθεί

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

Συχνά είναι χρήσιμο να πάρετε μια ανασκόπηση όλων των υποβολών που βρίσκονται σε αυτόν τον κλάδο, αλλά δεν βρίσκονται στον `master` σας.
Μπορείτε να αποκλείσετε υποβολές στον `master` σας προσθέτοντας την επιλογή `--not` πριν από το όνομα του κλάδου.
Αυτό είναι το ίδιο με τη μορφή `master..contrib` που χρησιμοποιήσαμε νωρίτερα.
Για παράδειγμα, εάν ο συνεισφέρων σάς στείλει δύο επιθέματα και δημιουργήσετε έναν κλάδο με το όνομα `contrib` και εφαρμόσετε αυτά τα επιθέματα εκεί, μπορείτε να τρέξετε το εξής:

[source,console]
-----
$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Oct 24 09:53:59 2008 -0700

    See if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Oct 22 19:38:36 2008 -0700

    Update gemspec to hopefully work better
-----

Για να δείτε τι αλλαγές εισάγει κάθε υποβολή, ας θυμηθείτε ότι μπορείτε να περάσετε την επιλογή `-p` στο `git log` και θα προσαρτήσει το αποτέλεσμα της diff που εισήχθη σε κάθε υποβολή.

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

[source,console]
-----
$ git diff master
-----

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

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

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

Από τεχνικής άποψης, αυτό μπορείτε να το καταφέρετε αν εντοπίσετε τον κοινό πρόγονο και στη συνέχεια τρέξετε τη διαφορά diff σε αυτό:

[source,console]
-----
$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db
-----

ή πιο συνεκτικά:

[source,console]

$ git diff $(git merge-base contrib master)

Ωστόσο, κανένα από τα δύο δεν είναι ιδιαίτερα βολικό, γι' αυτό το Git παρέχει μια άλλη συντόμευση για να κάνει το ίδιο πράγμα: τη σύνταξη με τις τρεις τελείες.
Στο πλαίσιο της εντολής `diff`, μπορείτε να βάλετε τρεις τελείες μετά από ένα άλλο κλάδο για να κάνετε ένα `diff` μεταξύ της τελευταίας υποβολής του κλάδου που βρισκόμαστε και του κοινού προγόνου της με έναν άλλο κλάδο:

[source,console]
-----
$ git diff master...contrib
-----

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

==== Ενσωμάτωση συνεισφερθείσας εργασίας

(((ενσωμάτωση εργασίας)))
Όταν όλη δουλειά στον θεματικό κλάδο σας είναι έτοιμη να ενσωματωθεί σε ένα πιο κεντρικό κλάδο, το ερώτημα που ανακύπτει είναι πώς να το κάνετε.
Επιπλέον, ποια ροή εργασίας θέλετε να χρησιμοποιήσετε για να συντηρήσετε το έργο σας;
Έχετε αρκετές επιλογές και στη συνέχεια θα καλύψουμε μερικές από αυτές.

===== Συγχώνευση ροών εργασίας

(((ροές εργασίας, συγχώνευση)))
Μια απλή ροή εργασίας είναι αυτή στην οποία συγχωνεύετε την εργασία σας στον κλάδο `master`.
Σε αυτό το σενάριο, έχετε ένα κύριο κλάδο που περιέχει κυρίως ευσταθή κώδικα.
Όταν εργάζεστε σε έναν θεματικό κλάδο που έχετε φτιάξει ή τον οποίο έχει συνεισφέρει κάποιος και έχετε επαληθεύσει, τον συγχωνεύετε στον `master` σας, διαγράφετε τον θεματικό κλάδο και στη συνέχεια συνεχίζετε τη διαδικασία.
Εάν έχετε ένα αποθετήριο με εργασία σε δύο κλάδους που ονομάζονται `ruby_client` και `php_client`, που μοιάζει με το <<rmerwf_a>>, και συγχωνεύσετε πρώτα τον `ruby_client` και στη συνέχεια τον `php_client`, τότε το ιστορικό σας θα καταλήξει να μοιάζει με το <<rmerwf_b>>.

[[rmerwf_a]]
.Ιστορικό με θεματικούς κλάδους.
image::images/merging-workflows-1.png[Ιστορικό με θεματικούς κλάδους.]

[[rmerwf_b]]
.Ιστορικό μετά από συγχώνευση θεματικών κλάδων.
image::images/merging-workflows-2.png[Ιστορικό μετά από συγχώνευση θεματικών κλάδων.]

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

Αν έχετε ένα πιο σημαντικό έργο, ίσως θέλετε να χρησιμοποιήσετε έναν κύκλο συγχώνευσης δύο φάσεων.
Σε αυτό το σενάριο έχετε δύο μακρόβιους κλάδους, `master` και `develop`, στους οποίους καθορίζετε ότι ο `master` ενημερώνεται μόνο όταν δημιουργείται μία πολύ σταθερή έκδοση και όλος ο νέος κώδικας είναι ενσωματωμένος στον κλάδο `develop`.
Ωθείτε και τους δύο αυτούς κλάδους τακτικά σε ένα δημόσιο αποθετήριο.
Κάθε φορά που έχετε έναν νέο θεματικό κλάδο να συγχωνεύσετε (<<rmerwf_c>>), τον συγχωνεύετε στον κλάδο `develop` (<<rmerwf_d>>)· τότε, όταν κάνετε tag μια έκδοση (release), μετακινείτε γρήγορα τον `master` σε όποιον κλάδο βρίσκεται ο ευσταθής κλάδος `develop` (<<rmerwf_e>>).

[[rmerwf_c]]
.Πριν από τη συγχώνευση θεματικού κλάδου.
image::images/merging-workflows-3.png[Πριν από τη συγχώνευση θεματικού κλάδου.]

[[rmerwf_d]]
.Μετά τη συγχώνευση θεματικού κλάδου.
image::images/merging-workflows-4.png[Μετά τη συγχώνευση θεματικού κλάδου.]

[[rmerwf_e]]
.Μετά τη δημοσιοποίηση έκδοσης.
image::images/merging-workflows-5.png[Μετά τη δημοσιοποίηση έκδοσης.]

Με αυτόν τον τρόπο, όταν κάποιος κλωνοποιεί το αποθετήριο του έργου σας, μπορεί είτε να μεταβεί στον κλάδο `master`, να χτίσει την πιο πρόσφατη ευσταθή έκδοση και να συμβαδίζει με αυτή εύκολα, είτε να μεταβεί στον κλάδο `develop`, που περιέχει τις πιο πρόσφατες εξελίξεις.
Μπορείτε επίσης να επεκτείνετε αυτό το μοντέλο και να έχετε έναν κλάδο ολοκλήρωσης `integrate` στο οποίο όλες οι εργασίες συγχωνεύονται.
Στη συνέχεια, όταν το codebase σε αυτόν τον κλάδο είναι ευσταθές και περνάει τα τεστ, μπορείτε να το συγχωνεύσετε σε έναν κλάδο `develop`· και όταν και αυτός έχει αποδειχθεί ευσταθής για κάποιο χρονικό διάστημα, τον ταχυπροωθείτε στον κύριο κλάδο σας.

===== Ροές εργασίας μεγάλης συγχώνευσης

(((ροές εργασίας, μεγάλης συγχώνευσης)))
Το έργο Git έχει τέσσερις μακρόβιους κλάδους: τους `master`,` next` και `seen` (παλιότερα `pu` -- proposed updates) για νέες εργασίες και τον `maint` για συντήρηση backport.
Όταν εισάγονται νέες εργασίες από συνεργάτες, συλλέγονται σε θεματικούς κλάδους στο αποθετήριο του διαχειριστή με τρόπο παρόμοιο με αυτόν που έχουμε περιγράψει (βλ. <<rmerwf_f>>).
Σε αυτό το σημείο, τα θέματα αξιολογούνται για να διαπιστωθεί αν είναι ασφαλή και έτοιμα προς κατανάλωση ή αν χρειάζονται περισσότερη δουλειά.
Αν είναι ασφαλή, συγχωνεύονται στον κλάδο `next` και αυτός ο κλάδος ωθείται, ώστε όλοι να μπορούν να δοκιμάσουν τα θέματα που ενσωματώθηκαν.

[[rmerwf_f]]
.Διαχείριση περίπλοκης ακολουθίας παράλληλων συνεισφερθέντων θεματικών κλάδων.
image::images/large-merges-1.png[Διαχείριση περίπλοκης ακολουθίας παράλληλων συνεισφερθέντων θεματικών κλάδων.]

Εάν τα θέματα θέλουν ακόμα δουλίτσα, συγχωνεύονται στον `seen`.
Όταν διαπιστωθεί ότι είναι τελείως ευσταθή, τα θέματα επανασυγχωνεύονται στον `master` και στη συνέχεια οι κλάδοι `next` και `seen` γίνονται ξανά build  από τον κλάδο `master`.
Αυτό σημαίνει ότι ο `master` προχωρά σχεδόν πάντα, ο `next` επανατοποθετείται περιστασιακά και ο `seen` επανατοποθετείται ακόμα πιο συχνά:

.Συγχώνευση συνεισφερθέντων θεματικών κλάδων σε μακρόβιους κλάδους ενσωμάτωσης.
image::images/large-merges-2.png[Συγχώνευση συνεισφερθέντων θεματικών κλάδων σε μακρόβιους κλάδους ενσωμάτωσης.]

Όταν ένας θεματικός κλάδος έχει τελικά συγχωνευτεί στον `master`, αφαιρείται από το αποθετήριο.
Το έργο Git διαθέτει επίσης έναν κλάδο `maint` που αποσχίζεται (forked) από την τελευταία δημοσιευμένη έκδοση (release) ώστε να παρέχει επιθέματα backport για την περίπτωση που απαιτείται έκδοση συντήρησης.
Έτσι, όταν κλωνοποιείτε το αποθετήριο Git, έχετε τέσσερις κλάδους στους οποίους μπορείτε να μεταβείτε για να αξιολογήσετε το έργο σε διαφορετικά στάδια ανάπτυξης, ανάλογα με το πόσο αιχμής θέλετε να είστε ή πώς θέλετε να συνεισφέρετε· και ο συντηρητής έχει μια δομημένη ροή εργασίας για να τους βοηθήσει να ελέγξουν νέες συνεισφορές.

[[r_rebase_cherry_pick]]
===== Ροές εργασίας με αλλαγή βάσης και ανθολόγηση

(((workflows, rebasing and cherry-picking)))
Άλλοι συντηρητές προτιμούν να επανατοποθετούν (rebase) ή να ανθολογούν (cherry-pick) τις συνεισφορές στην κορυφή του κύριου κλάδου τους, αντί να τις συγχωνεύουν, για να διατηρήσουν ένα κυρίως γραμμικό ιστορικό.
Όταν εργάζεστε σε έναν θεματικό κλάδο και έχετε αποφασίσει ότι θέλετε να τον ενσωματώσετε, μεταβαίνετε σε αυτόν και εκτελείτε την εντολή `rebase` για να ξαναχτίσετε τις αλλαγές με νέα βάση τον `master` σας (ή τον κλάδο `develop` κ.ο.κ.).
Αν αυτό λειτουργεί καλά, τότε μπορείτε να ταχυπροωθήσετε τον κύριο κλάδο σας οπότε θα καταλήξετε με ένα γραμμικό ιστορικό έργου.


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

.Παράδειγμα ιστορικού πριν την ανθολόγηση.
image::images/rebasing-1.png[Παράδειγμα ιστορικού πριν την ανθολόγηση.]

Αν θέλετε να ελκύσετε την υποβολή `e43a6` στον `master`, μπορείτε να εκτελέσετε:

[source,console]
-----
$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)
-----

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

.Ιστορικό μετά την ανθολόγηση υποβολής σε έναν θεματικό κλάδο.
image::images/rebasing-2.png[Ιστορικό μετά την ανθολόγηση υποβολής σε έναν θεματικό κλάδο.]

Πλέον μπορείτε να καταργήσετε τον θεματικό κλάδο και να εγκαταλείψετε τις υποβολές που δεν θέλατε να ελκύσετε.

===== Rerere

(((εντολές git, rerere)))(((rerere)))
Εάν κάνετε πολλές συγχωνεύσεις και αλλαγές βάσης ή διατηρείτε ένα μακρόβιο θεματικό κλάδο, το Git διαθέτει μια λειτουργία που λέγεται `rerere` και η οποία μπορεί να σας βοηθήσει.

`rerere` σημαίνει "`reuse recorded resolution`" ("`επαναχρησιμοποίηση καταγεγραμμένης επίλυσης`") -- είναι ένας τρόπος σύντομης αντιμετώπισης της μη-αυτόματης επίλυσης συγκρούσεων.
Όταν η rerere είναι ενεργοποιημένη, το Git θα διατηρήσει ένα σύνολο εικόνων πριν και μετά από επιτυχείς συγχωνεύσεις και αν παρατηρήσει ότι υπάρχει μια σύγκρουση που μοιάζει ακριβώς με μία που έχετε ήδη επιλύσει, θα χρησιμοποιήσει απλώς το επίθεμα από την τελευταία φορά, χωρίς να σας ενοχλήσει.

Αυτό το χαρακτηριστικό αποτελείται από δύο μέρη: μια παραμετροποίηση και μια εντολή.
Η παραμετροποίηση είναι `rerere.enabled` και είναι αρκετά βολική ώστε να την έχετε καθολικό σας αρχείο config:

[source,console]

$ git config --global rerere.enabled true

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

Αν χρειαστεί, μπορείτε να αλληλεπιδράσετε με τη μνήμη cache rerere χρησιμοποιώντας την εντολή `git rerere`.
Όταν καλείται χωρίς διακόπτες, το Git ελέγχει τη βάση δεδομένων επιλύσεων και προσπαθεί να βρει μια αντιστοίχιση με τις τρέχουσες συγκρούσεις συγχώνευσης και να τις επιλύσει (αν και αυτό γίνεται αυτόματα αν το `rerere.enabled` οριστεί σε `true`).
Υπάρχουν επίσης δευτερεύουσες εντολές για να δείτε τι θα εγγραφεί, να διαγράψετε συγκεκριμένη ανάλυση από την προσωρινή μνήμη και να καθαρίσετε ολόκληρη την προσωρινή μνήμη (cache).
Θα καλύψουμε την rerere με περισσότερες λεπτομέρειες στην ενότητα <<rch_rerere>>.

[[r_tagging_releases]]
==== Δημιουργία ετικετών για τις δημοσιευμένες εκδόσεις

(((tags)))(((tags, signing)))
Όταν αποφασίσετε να δημοσιοποίησετε μια έκδοση, πιθανώς θέλετε να αφήσετε μια ετικέτα ώστε να μπορείτε να δημιουργήσετε εκ νέου αυτή την έκδοση σε οποιοδήποτε σημείο στο μέλλον.
Μπορείτε να δημιουργήσετε μια νέα ετικέτα όπως περιγράφεται στην ενότητα <<ch02-git-basics>>.
Αν αποφασίσετε να υπογράψετε την ετικέτα ως ο συντηρητής, η ετικέτα μπορεί να φαίνεται κάπως έτσι:

[source,console]
-----
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09
-----

Εάν υπογράφετε τις ετικέτες σας, μπορεί να έχετε πρόβλημα όσον αφορά στη διανομή του δημόσιου κλειδιού PGP που χρησιμοποιείται για την υπογραφή των ετικετών σας.
Ο συντηρητής του έργου Git έχει επιλύσει αυτό το ζήτημα συμπεριλαμβάνοντας το δημόσιο κλειδί του ως ένα blob στο αποθετήριο και στη συνέχεια προσθέτοντας μια ετικέτα που δείχνει κατευθείαν σε αυτό το περιεχόμενο.
Για να το κάνετε αυτό, πρέπει να καταλάβετε ποιο κλειδί θέλετε εκτελώντας την εντολή `gpg --list-keys`:

[source,console]
-----
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon <schacon@gmail.com>
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]
-----

Στη συνέχεια, μπορείτε να εισάγετε απευθείας το κλειδί στη βάση δεδομένων Git, αν το εξάγετε και το παροχετεύσετε στο `git hash-object`, το οποίο γράφει ένα νέο blob με αυτά τα περιεχόμενα στο Git και σας επιστρέφει τον SHA-1 του blob:

[source,console]
-----
$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92
-----

Τώρα που έχετε τα περιεχόμενα του κλειδιού σας στο Git, μπορείτε να δημιουργήσετε μια ετικέτα που να δείχνει απευθείας σε αυτό δίνοντας τη νέα τιμή SHA-1 που σας έδωσε η εντολή `hash-object`:

[source,console]
-----
$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92
-----

Εάν εκτελέσετε `git push --tags`, η ετικέτα `maintainer-pgp-pub` θα κοινοποιηθεί σε όλους.
Αν κάποιος θέλει να επαληθεύσει μια ετικέτα, μπορεί να εισάγει απευθείας το PGP κλειδί σας τραβώντας το blob απευθείας από τη βάση δεδομένων και εισάγοντάς το στο GPG:

[source,console]
-----
$ git show maintainer-pgp-pub | gpg --import
-----

Μπορεί να χρησιμοποιήσει αυτό το κλειδί για να ελέγξει όλες τις ετικέτες που έχετε υπογράψει.
Επίσης, αν συμπεριλάβετε οδηγίες στο μήνυμα της ετικέτας, η λειτουργία `git show <ετικέτα>` θα σας επιτρέψει να δώσετε στον τελικό χρήστη πιο συγκεκριμένες οδηγίες σχετικά με την επαλήθευση ετικετών.

[[r_build_number]]
==== Παραγωγή αριθμού build

(((αριθμοί build)))(((εντολές git, describe)))
Επειδή το Git δεν έχει αύξοντες αριθμούς όπως `v123` ή το ισοδύναμο για τις υποβολές, αν θέλετε να έχετε ένα ανθρωπανάγνωστο όνομα για κάθε υποβολή, μπορείτε να εκτελέσετε `git describe` σε αυτή την υποβολή.
Το Git σάς δίνει το όνομα της πλησιέστερης χρονικά ετικέτας με τον αριθμό υποβολών στην κορυφή της ετικέτας και τη μερική τιμή SHA-1 της υποβολής που περιγράφετε (με τον χαρακτήρα "`g`" στην αρχή, που σημαίνει Git):

[source,console]
-----
$ git describe master
v1.6.2-rc1-20-g8c5b85c
-----

Με αυτό τον τρόπο, μπορείτε να εξάγετε ένα στιγμιότυπο ή build και να τα ονομάσετε με κάτι κατανοητό από ανθρώπους και όχι από μηχανήματα.
Μάλιστα, αν δημιουργήσετε το Git από τον πηγαίο κώδικά του, που έχει κλωνοποιηθεί από το αποθετήριο Git, το `git --version` σας δίνει κάτι που μοιάζει με αυτό.
Αν περιγράφετε μια υποβολή, στην οποία έχετε προσαρτήσει μια ετικέτα, σας δίνει το όνομα της ετικέτας.

Η εντολή `git describe` απαιτεί επισημασμένες ετικέτες (ετικέτες που δημιουργούνται με τις σημαίες `-a` ή `-s`).
Αν θέλετε να χρησιμοποιήσετε και τις απλές (μη-επισημασμένες) ετικέτες, προσθέστε την επιλογή `--tags` στην εντολή.
Μπορείτε επίσης να χρησιμοποιήσετε αυτή τη συμβολοσειρά ως τον στόχο μιας εντολής `git checkout` ή `git show` αν και βασίζεται στη συντομευμένη τιμή SHA-1 (τα τελευταία ψηφία), οπότε ίσως να μην ισχύει για πάντα.
Για παράδειγμα, ο πυρήνας Linux αυξήθηκε πρόσφατα από 8 σε 10 χαρακτήρες για να εξασφαλίσει τη μοναδικότητα αντικειμένων SHA-1, με αποτέλεσμα τα παλαιότερα ονόματα που δημιουργήθηκαν από την `git describe` να μην είναι έγκυρα πλέον.

[[r_preparing_release]]
==== Προετοιμασία μίας δημοσιευμένης έκδοσης

(((δημοσίευση έκδοσης (release) )))(((εντολές git, archive)))
Τώρα θέλετε να δημοσιεύσετε μία build.
Ένα από τα πράγματα που θα θελήσετε να κάνετε είναι να δημιουργήσετε ένα αρχείο (archive) του τελευταίου στιγμιότυπου του κώδικά σας για τις φτωχές ψυχές που δεν χρησιμοποιούν το Git.
Η εντολή για να γίνει αυτό είναι `git archive`:

[source,console]
-----
$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz
-----

Αν κάποιος ανοίξει αυτό το tarball, θα πάρει το τελευταίο στιγμιότυπο του έργου σας μέσα σε έναν κατάλογο με όνομα `project`.
Μπορείτε επίσης να δημιουργήσετε ένα αρχείο zip με τον ίδιο τρόπο, αν περάσετε την επιλογή `--format = zip` στην `git archive`:

[source,console]
-----
$ git archive master --prefix='project/' --format=zip > `git describe master`.zip
-----

Τώρα έχετε ένα ωραιότατο tarball και ένα αρχείο zip της έκδοσης του έργου σας που μπορείτε να ανεβάσετε στον ιστότοπό σας ή να στείλετε με e-mail σε άλλους.

[[r_the_shortlog]]
==== Η εντολή `shortlog`

(((εντολές git, shortlog)))
Ήρθε η ώρα να στείλετε e-mail στα μέλη της mailing list που θέλουν να μάθουν τι συμβαίνει στο έργο σας.
Ένας καλός τρόπος να αποκτήσετε γρήγορα ένα είδος μητρώου αλλαγών (changelog) από ό,τι έχει προστεθεί στο έργο σας από την τελευταία έκδοση ή το e-mail σας είναι να χρησιμοποιήσετε την εντολή `git shortlog`.
Συνοψίζει όλες τις υποβολές στο εύρος υποβολών που της δίνετε· για παράδειγμα, παρακάτω δίνεται μια περίληψη όλων των υποβολών από την τελευταία έκδοση, εάν η τελευταία έκδοσή σας ονομάστηκε v1.0.1:

[source,console]
-----
$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (6):
      Add support for annotated tags to Grit::Tag
      Add packed-refs annotated tag support.
      Add Grit::Commit#to_patch
      Update version and History.txt
      Remove stray `puts`
      Make ls_tree ignore nils

Tom Preston-Werner (4):
      fix dates in history
      dynamic version method
      Version bump to 1.0.2
      Regenerated gemspec for version 1.0.2
-----

Παίρνετε μια καθαρή σύνοψη όλων των υποβολών από την v1.0.1 και μετά, ομαδοποιημένων κατά συγγραφέα, που μπορείτε να στείλετε με e-mail στη λίστα σας.



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

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


[#ch06-github]
[[r_github]]
== GitHub

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

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

Εάν δεν ενδιαφερόμαστε να χρησιμοποιήσουμε το GitHub για να φιλοξενήσουμε τα δικά μας έργα ή για να συνεργαστούμε με άλλα έργα που φιλοξενούνται στο GitHub, μπορούμε να μεταβούμε με ασφάλεια στο κεφάλαιο <<ch07-git-tools>>.


[WARNING]
.Αλλαγή διεπαφών
====
Είναι σημαντικό να σημειωθεί ότι όπως σε πολλούς άλλους ενεργούς ιστοτόπους, τα στοιχεία UI των στιγμιότυπων οθόνης θα αλλάξουν με την πάροδο του χρόνου.
Ας ελπίσουμε ότι η γενική ιδέα του τι προσπαθούμε να πετύχουμε εδώ θα εξακολουθεί να είναι εκεί, αλλά για πιο πρόσφατες εκδόσεις αυτών των οθονών, οι online εκδόσεις αυτού του βιβλίου ενδέχεται να έχουν νεότερα στιγμιότυπα οθόνης.
====

=== Δημιουργία λογαριασμού και ρύθμισή του

(((GitHub, user accounts)))
Το πρώτο πράγμα που πρέπει να κάνουμε είναι να δημιουργήσουμε έναν δωρεάν λογαριασμό χρήστη.
Απλά επισκεφτόμαστε τη διεύθυνση https://github.com[], επιλέγουμε ένα όνομα χρήστη που δεν το έχει πάρει κάποιος άλλος, δίνουμε μια διεύθυνση e-mail και έναν κωδικό πρόσβασης και κάνουμε κλικ στο μεγάλο πράσινο κουμπί ``Sign up for GitHub''.

.Η φόρμα εγγραφής του GitHub.
image::images/signup.png[Η φόρμα εγγραφής του GitHub.]

Το επόμενο πράγμα που θα δούμε είναι η σελίδα τιμολόγησης για αναβαθμισμένα πακέτα, αλλά είναι ασφαλές να την αγνοήσουμε προς το παρόν.
Το GitHub θα μας στείλει ένα μήνυμα e-mail για να επαληθεύσουμε τη διεύθυνση που δώσαμε.
Ας το κάνουμε· είναι πολύ σημαντικό όπως θα δούμε αργότερα.

[NOTE]
====
Το GitHub παρέχει όλες τις λειτουργίες του με δωρεάν λογαριασμούς, με τον περιορισμό ότι όλα τα έργα μας είναι πλήρως δημόσια (όλοι έχουν πρόσβαση ανάγνωσης).
Τα πακέτα του GitHub με πληρωμή περιλαμβάνουν έναν ορισμένο αριθμό ιδιωτικών έργων, αλλά δεν θα τα καλύψουμε σε αυτό το βιβλίο.
====

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

==== Πρόσβαση με SSH

(((κλειδιά SSH, with GitHub)))
Ήδη μπορούμε να συνδεθούμε πλήρως με αποθετήρια Git χρησιμοποιώντας το πρωτόκολλο `https://`, και να ταυτοποιηθούμε με το όνομα χρήστη και τον κωδικό πρόσβασης που μόλις δημιουργήσαμε.
Πάντως, αν θέλουμε μόνον να κλωνοποιήσουμε δημόσια έργα, δεν χρειάζεται καν να συνδεθούμε —ο λογαριασμός που μόλις δημιουργήσαμε θα χρειαστεί όταν αποσχίσουμε κάποιο έργο και ωθήσουμε στις διχάλες μας αργότερα.

Εάν θέλουμε να χρησιμοποιήσουμε απομακρυσμένα αποθετήρια μέσω SSH, θα πρέπει να δημιουργήσουμε ένα δημόσιο κλειδί.
(Εάν δεν έχουμε ήδη ένα, ανατρέχουμε στην ενότητα <<r_generate_ssh_key>>.)
Ανοίγουμε τις ρυθμίσεις του λογαριασμού μας χρησιμοποιώντας το σύνδεσμο ``Account settings'' που βρίσκεται στην επάνω δεξιά γωνία του παραθύρου:

[[raccount_settings]]
.Ο σύνδεσμος ``Account settings''.
image::images/account-settings.png[Ο σύνδεσμος ``Account settings''.]

Στη συνέχεια, επιλέγουμε την ενότητα ``SSH keys'' στην αριστερή πλευρά.

[[rssh_keys]]
.Ο σύνδεσμος ``SSH keys''.
image::images/ssh-keys.png[Ο σύνδεσμος ``SSH keys''.]

Από εκεί, κάνουμε κλικ στο κουμπί ``Add an SSH key'', δίνουμε στο κλειδί μας ένα όνομα, επικολλούμε τα περιεχόμενα του αρχείου δημόσιου κλειδιού (`~/.ssh/id_rsa.pub` ή όπως αλλιώς το έχουμε ονομάσει) και κάνουμε κλικ στο κουμπί ``Add key''.


[NOTE]
====
Είναι σημαντικό να ονομάζουμε το κλειδί SSH με ένα όνομα που μπορούμε να θυμηθούμε.
Μπορούμε να ονομάσουμε καθένα από τα κλειδιά μας (π.χ. ``My Laptop'' ή ``Work Account''), έτσι ώστε αν χρειαστεί να ανακαλέσουμε ένα κλειδί αργότερα, να μπορούμε εύκολα να πούμε ποιο αναζητούμε.
====

[[r_personal_avatar]]
==== Το avatar

Στη συνέχεια, αν το επιθυμούμε, μπορούμε να αντικαταστήσουμε το avatar που δημιουργήθηκε για εμάς με μια εικόνα της επιλογής μας.
Πρώτα πηγαίνουμε στην καρτέλα ``Profile'' (πάνω από την καρτέλα ``SSH Keys'') και κάνουμε κλικ στο ``Upload new picture''.

.Ο σύνδεσμος ``Profile''.
image::images/your-profile.png[Ο σύνδεσμος ``Profile''.]

Θα επιλέξουμε ένα αντίγραφο του λογότυπου Git που βρίσκεται στο σκληρό δίσκο μας και στη συνέχεια θα έχουμε την ευκαιρία να τον περικόψουμε.

.Περικοπή του avatar.
image::images/avatar-crop.png[Περικοπή του μεταφορτωμένου avatar.]

Τώρα οπουδήποτε αλληλεπιδράμε στον ιστότοπο, οι χρήστες θα βλέπουν το avatar μας δίπλα στο όνομα χρήστη μας.

Αν τυχαίνει να έχουμε ανεβάσει ένα avatar στη δημοφιλή υπηρεσία Gravatar (που χρησιμοποιείται συχνά για λογαριασμούς Wordpress), αυτό το avatar θα χρησιμοποιηθεί εκ προεπιλογής και δεν χρειάζεται να κάνουμε αυτό το βήμα.

==== Οι διευθύνσεις μας e-mail

Ο τρόπος με τον οποίο το GitHub αντιστοιχίζει τις υποβολές μας στον χρήστη που είμαστε είναι μέσω διεύθυνσης e-mail μας.
Εάν χρησιμοποιούμε πολλές διευθύνσεις e-mail στις υποβολές μας και θέλουμε το GitHub να τις συνδέσει σωστά, θα πρέπει να προσθέσουμε όλες τις διευθύνσεις e-mail που έχουμε χρησιμοποιήσει στην ενότητα Emails της ενότητας admin.

[[r_add_email_addresses]]
.Προσθήκη διευθύνσεων e-mail
image::images/email-settings.png[Προσθήκη όλων των διευθύνσεων e-mail.]

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


==== Ταυτοποίηση δύο παραγόντων

Τέλος, για επιπρόσθετη ασφάλεια, θα πρέπει σίγουρα να ορίσουμε _ταυτοποίηση δύο παραγόντων_ (``Two-factor authentication'') ή ``2FA''.
Η ταυτοποίηση δύο παραγόντων είναι ένας μηχανισμός ταυτοποίησης που γίνεται ολοένα και πιο δημοφιλής και μετριάζει τον κίνδυνο να εκτεθεί ο λογαριασμός μας, αν κάποιος καταφέρει και κλέψει τον κωδικό πρόσβασής μας.
Αν την ενεργοποιήσουμε, το GitHub θα μας ζητήσει δύο διαφορετικές μεθόδους ταυτοποίησης, έτσι ώστε εάν κάποια από αυτές παραβιαστεί, ο εισβολέας δεν θα μπορέσει να αποκτήσει πρόσβαση στον λογαριασμό μας.

Μπορούμε να βρούμε τη ρύθμιση ``Two-factor Authentication'' στην καρτέλα ``Security'' των ρυθμίσεων του λογαριασμού μας.

[[rsecurity_2fa]]
.2FA στην καρτέλα Security.
image::images/2fa-1.png[2FA στην καρτέλα ``Security''.]

Εφόσον κάνουμε κλικ στο κουμπί ``Set up two-factor authentication'', θα μεταβούμε σε μια σελίδα διαμόρφωσης όπου μπορούμε να επιλέξουμε να χρησιμοποιήσουμε μια εφαρμογή τηλεφώνου για να δημιουργήσουμε τον δευτερεύοντα κωδικό μας (έναν ``κωδικό πρόσβασης μίας χρήσης περιορισμένης χρονικής διάρκειας'') ή μπορούμε να ζητήσουμε από το GitHub να μας στέλνει έναν κωδικό μέσω SMS κάθε φορά που θέλουμε να συνδεθούμε.

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



=== Συνεισφορά σε έργο

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

==== Αποσχισμένα έργα

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

[NOTE]
====
Ιστορικά, ο όρος ``διχάλα'' (fork) είχε αρνητική χροιά, σήμαινε ότι κάποιος πήρε ένα έργο ανοιχτού κώδικα προς μια διαφορετική κατεύθυνση, δημιουργώντας μερικές φορές ένα ανταγωνιστικό έργο και χωρίζοντας τους συνεισφέροντες.
Στο GitHub, μία ``διχάλα'' είναι απλά το ίδιο έργο στον δικό μας ονοματοχώρο, που μας επιτρέπει να κάνουμε αλλαγές σε ένα έργο δημοσίως, ένας τρόπος να συμβάλλουμε με έναν πιο ανοιχτό τρόπο.
====

Με αυτόν τον τρόπο, τα έργα δεν χρειάζεται να ανησυχούν για την προσθήκη χρηστών ως συνεργατών για να τους δώσουν πρόσβαση ώθησης.
Οι συνεργάτες μπορούν να αποσχίσουν ένα έργο, να ωθήσουν σε αυτό και να συμβάλουν τις αλλαγές τους στο αρχικό αποθετήριο δημιουργώντας κάτι που ονομάζεται _αίτημα έλξης_ (pull request), το οποίο θα καλύψουμε στη συνέχεια.
Το αίτημα έλξης ανοίγει ένα νήμα συζήτησης με αναθεώρηση κώδικα, στο οποίο ο ιδιοκτήτης και ο συνεισφέρων μπορούν στη συνέχεια να επικοινωνούν σχετικά με τις αλλαγές μέχρι ο ιδιοκτήτης να ικανοποιηθεί από αυτές, οπότε μπορεί να τις συγχωνεύσει.

Για να αποσχίσουμε ένα έργο, επισκεφτόμαστε τη σελίδα του έργου και κάνουμε κλικ στο κουμπί ``Fork'' στο πάνω δεξί μέρος της σελίδας.

.Το κουμπί ``Fork''.
image::images/forkbutton.png[Το κουμπί ``Fork''.]

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


[[r_github_flow]]
==== Η ροή εργασίας του GitHub

(((GitHub, ροή)))
Το GitHub είναι σχεδιασμένο γύρω από μια συγκεκριμένη συνεργατική ροή εργασίας, με επίκεντρο τα αιτήματα έλξης.
Αυτή η ροή λειτουργεί είτε συνεργαζόμαστε με μια ομάδα με μεγάλη συνοχή σε ένα κοινό αποθετήριο, είτε με μια εταιρεία διασκορπισμένη σε όλο τον κόσμο, είτε ένα δίκτυο αγνώστων μεταξύ τους που συμβάλλουν σε ένα έργο μέσω δεκάδων διχαλών.
Στο επίκεντρό της έχει τη ροή εργασιών της ενότητας <<r_topic_branch>> που καλύπτεται στο κεφάλαιο <<ch03-git-branching>>.

Ας δούμε πώς λειτουργεί γενικά:

1. Δημιουργούμε έναν θεματικό κλάδο από τον κλάδο `master`.
2. Κάνουμε ορισμένες υποβολές ώστε αν βελτιώσουμε το έργο.
3. Ωθούμε αυτόν τον κλάδο στο έργο του GitHub.
4. Υποβάλλουμε ένα αίτημα έλξης στο GitHub.
5. Συζητάμε και, προαιρετικά, συνεχίζουμε να υποβάλλουμε.
6. Ο ιδιοκτήτης του έργου συγχωνεύει τον κλάδο ή κλείνει το αίτημα έλξης.

Αυτή είναι βασικά η ροή εργασίας με διαχειριστή ενσωμάτωσης που καλύπτεται στην ενότητα <<r_integration_manager>>, αλλά οι ομάδες, αντί να χρησιμοποιούν email για να επικοινωνούν και να αναθεωρούν τις αλλαγές, χρησιμοποιούν τα εργαλεία της ιστοσελίδας του GitHub.

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

===== Δημιουργία αιτήματος έλξης

Ο Tony ψάχνει κώδικα για να τρέξει στον προγραμματιζόμενο μικροελεγκτή του, Arduino, και βρήκε ένα εξαιρετικό πρόγραμμα στο GitHub στη διεύθυνση https://github.com/schacon/blink[].

.Το έργο στο οποίο θέλουμε να συμβάλλουμε.
image::images/blink-01-start.png[Το έργο στο οποίο θέλουμε να συμβάλλουμε.]

Το μόνο πρόβλημα είναι ότι ο ρυθμός με τον οποίο αναβοσβήνει το φωτάκι είναι πολύ γρήγορος.
Θεωρούμε ότι θα ήταν πολύ καλύτερα αν περιμένε 3 δευτερόλεπτα αντί για 1 μεταξύ κάθε αλλαγής κατάστασης.
Ας βελτιώσουμε λοιπόν το πρόγραμμα και ας υποβάλουμε τη βελτίωση στο έργο ως μια προτεινόμενη αλλαγή.

Πρώτα, κάνουμε κλικ στο κουμπί ``Fork'', όπως αναφέρθηκε προηγουμένως, για να λάβουμε το δικό μας αντίγραφο του έργου.
Το όνομα χρήστη εδώ είναι `tonychacon`, οπότε το αντίγραφο αυτού του έργου είναι στη διεύθυνση `https://github.com/tonychacon/blink` και εκεί μπορούμε να το επεξεργαστούμε.
Θα το κλωνοποιήσουμε τοπικά, θα δημιουργήσουμε έναν θεματικό κλάδο, θα κάνουμε αλλαγές στον κώδικα και τέλος θα ωθήσουμε αυτήν την αλλαγή πίσω στο GitHub.

[source,console]

$ git clone https://github.com/tonychacon/blink <1> Cloning into blink…​

$ cd blink $ git checkout -b slow-blink <2> Switched to a new branch slow-blink

$ sed -i ' 's/1000/3000/ blink.ino <3>

$ git diff --word-diff <4> diff --git a/blink.ino b/blink.ino index 15b9911..a6cc5a5 100644 --- a/blink.ino + b/blink.ino @@ -18,7 18,7 @@ void setup() { void loop() { digitalWrite(led, HIGH); // άναψε το LED (HIGH είναι η τάση) [-delay(1000);-]{+delay(3000);} // περίμενε 1 δευτερόλεπτο digitalWrite(led, LOW); // σβήσε το LED κάνοντας την τάση LOW [-delay(1000);-]{delay(3000);} // περίμενε 1 δευτερόλεπτο }

$ git commit -a -m three seconds is better <5> [slow-blink 5ca509d] three seconds is better 1 file changed, 2 insertions(+), 2 deletions(-)

$ git push origin slow-blink <6> Username for https://github.com: tonychacon Password for https://tonychacon@github.com: Counting objects: 5, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) To https://github.com/tonychacon/blink * [new branch] slow-blink → slow-blink

<1> Κλωνοποιούμε τη διχάλα του έργου σε τοπικό επίπεδο
<2> Δημιουργούμε έναν περιγραφικό θεματικό κλάδο
<3> Κάνουμε την αλλαγή που θέλουμε στον κώδικα
<4> Ελέγχουμε ότι η αλλαγή είναι καλή
<5> Υποβάλλουμε την αλλαγή στον θεματικό κλάδο
<6> Ωθούμε τον νέο θεματικό κλάδο στη διχάλα μας στο GitHub

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

Εναλλακτικά, μπορούμε να μεταβούμε στη σελίδα ``branches'' στη διεύθυνση `https://github.com/<χρήστης>/<έργο>/branches` για να εντοπίσουμε τον κλάδο μας και να υποβάλουμε ένα νέο αίτημα έλξης από εκεί.

.Κουμπί ``Pull request''.
image::images/blink-02-pr.png[Κουμπί ``Pull request''.]

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

Επίσης, βλέπουμε μια λίστα των υποβολών μας στον θεματικό κλάδο μας που ``προηγείται'' του κλάδου `master` (στην περίπτωση αυτή, μόνο κατά μία υποβολή) και ένα ενοποιημένο diff όλων των αλλαγών που θα γίνουν σε περίπτωση που αυτός ο κλάδος συγχωνευτεί από τον ιδιοκτήτη του έργου.

.Σελίδα δημιουργίας αιτήματος έλξης.
image::images/blink-03-pull-request-open.png[Σελίδα δημιουργίας αιτήματος έλξης]

Όταν πατήσουμε το κουμπί ``Create pull request'' σε αυτήν την οθόνη, ο ιδιοκτήτης του έργου, από το οποίο αποσχιστήκαμε, θα λάβει ειδοποίηση ότι κάποιος προτείνει μια αλλαγή και θα συνδεθεί σε μια σελίδα που περιέχει όλες αυτές τις πληροφορίες.

[NOTE]
====
Παρόλο που τα αιτήματα έλξης χρησιμοποιούνται συνήθως για δημόσια έργα όπως αυτό, στο οποίο ο συνεισφέρων έχει μια πλήρη αλλαγή έτοιμη προς υλοποίηση, επίσης συχνά χρησιμοποιείται σε ιδιωτικά έργα _στην αρχή_ του κύκλου ανάπτυξης. Επειδή μπορούμε να συνεχίσουμε να ωθούμε στον θεματικό κλάδο *ακόμα και μετά* την υποβολή του αιτήματος έλξης, η υποβολή του αιτήματος έλξης δεν γίνεται στο τέλος της διαδικασίας, αλλά νωρίς και αποτελεί έναν τρόπο σταδιακής βελτίωσης της εργασίας με συνεισφορές από όλη την ομάδα.
====

===== Επανάληψη αιτήματος έλξης

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

Ενώ αυτή η συζήτηση πραγματοποιείται μέσω email στις ροές εργασίας που παρουσιάζονται στην ενότητα <<ch05-distributed-git>>, στο GitHub αυτό συμβαίνει στο διαδίκτυο. Ο ιδιοκτήτης του έργου μπορεί να ελέγξει το ενοποιημένο diff και να αφήσει ένα σχόλιο κάνοντας κλικ σε οποιαδήποτε από τις γραμμές.

.Σχόλιο σε οποιαδήποτε γραμμή του κώδικα σε αίτημα έλξης.
image::images/blink-04-pr-comment.png[Σχόλιο σε γραμμή κατά το αίτημα έλξης]

Μόλις ο διαχειριστής κάνει αυτό το σχόλιο, ο χρήστης που υπέβαλε το αίτημα έλξης (όπως και οποιοσδήποτε άλλος παρακολουθεί το αποθετήριο) θα λάβει μια ειδοποίηση. Θα δούμε πώς αυτό είναι δυνατό να προσωποποιηθεί αργότερα, αλλά εάν είχε ενεργοποιημένες τις ειδοποιήσεις μέσω email, ο Tony θα λάμβανε ένα μήνυμα όπως αυτό:

[[r_email_notification]]
.Σχόλια που στέλνονται ως email.
image::images/blink-04-email.png[Σχόλια που στέλνονται ως email]

Οποιοσδήποτε μπορεί να αφήσει γενικά σχόλια σχετικά με το αίτημα έλξης. Στην εικόνα <<r_pr_discussion>> μπορούμε να δούμε ένα παράδειγμα ενός ιδιοκτήτη έργου που σχολιάζει μια γραμμή κώδικα και στη συνέχεια αφήνει ένα γενικό σχόλιο στο τμήμα συζήτησης. Μπορούμε να δούμε ότι τα σχόλια του κώδικα συμπεριλαμβάνονται και στη συνομιλία.

[[r_pr_discussion]]
.Σελίδα συζήτησης αιτήματος
image::images/blink-05-general-comment.png[Σελίδα συζήτησης αιτήματος]

Τώρα ο συνεισφέρων μπορεί να δει τι πρέπει να κάνει για να γίνει αποδεκτή η αλλαγή του. Ευτυχώς αυτό είναι επίσης πολύ απλό. Ενώ μέσω ηλεκτρονικού ταχυδρομείου θα χρειαζόταν να αναιρέσουμε τη σειρά αλλαγών και να την υποβάλουμε εκ νέου στην ηλεκτρονική λίστα αλληλογραφίας, με το GitHub απλά ξαναϋποβάλουμε (commit) τον θεματικό κλάδο και τον ξαναωθούμε (push). Αυτό θα ενημερώσει αυτόματα το αίτημα έλξης. Στην εικόνα <<r_pr_final>>, βλέπουμε επίσης ότι το παλιό σχόλιο έχει συμπτυχθεί στο ενημερωμένο αίτημα έλξης, διότι είχε γίνει για μία γραμμή που πλέον έχει τροποποιηθεί.

[[r_pr_final]]
.Τελικό αίτημα έλξης
image::images/blink-06-final.png[Τελικό αίτημα έλξης]

Κάτι ενδιαφέρον, που πρέπει να παρατηρήσουμε, είναι ότι αν κάνουμε κλικ στην καρτέλα ``Files changed'' σε αυτό το αίτημα έλξης, θα πάρουμε την ``ενοποιημένη'' diff —δηλαδή, τη συνολική αθροιστικά διαφορά που θα εισαγόταν στον κύριο κλάδο αν αυτός ο θεματικός κλάδος συγχωνευόταν. Με όρους `git diff`, ουσιαστικά μας δείχνει αυτόματα `git diff master...<κλάδος>` για τον κλάδο στον οποίο βασίζεται αυτό το αίτημα έλξης. Περισσότερες πληροφορίες σχετικά με αυτό το είδος diff υπάρχουν στην ενότητα <<r_what_is_introduced>>.

Το άλλο που πρέπει να παρατηρήσουμε είναι ότι το GitHub ελέγχει εάν το αίτημα έλξης συγχωνεύεται χωρίς συγκρούσεις και σε αυτήν την περίπτωση μας παρέχει ένα κουμπί για να κάνουμε τη συγχώνευση στον διακομιστή. Αυτό το κουμπί εμφανίζεται μόνο αν έχουμε πρόσβαση εγγραφής στο αποθετήριο και αν είναι δυνατή μια τετριμμένη συγχώνευση. Εάν κάνουμε κλικ σ' αυτό το κουμπί, το GitHub θα εκτελέσει μια συγχώνευση ``non-fast forward'', κάτι που σημαίνει ότι ακόμα και αν η συγχώνευση *θα μπορούσε* να είναι ταχυπροώθηση, θα δημιουργήσει μια υποβολή συγχώνευσης.

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

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

[NOTE]
.Όχι μόνον απόσχιση
====
Είναι σημαντικό να σημειώσουμε ότι μπορούμε επίσης να ανοίξουμε ένα αίτημα έλξης μεταξύ δύο κλάδων στο ίδιο αποθετήριο. Εάν εργαζόμαστε σε ένα χαρακτηριστικό με κάποιον και έχουμε και οι δύο πρόσβαση για εγγραφή στο έργο, μπορούμε να ωθήσουμε έναν θεματικό κλάδο στο αποθετήριο και να υποβάλουμε ένα αίτημα έλξης του από τον κλάδο `master` του ίδιου έργου, ώστε να ξεκινήσει η αναθεώρηση κώδικα και η διαδικασία συζήτησης. Η απόσχιση δεν είναι απαραίτητη.
====

==== Προχωρημένα αιτήματα έλξης

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

===== Αιτήματα έλξης ως επιθέματα

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

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

Για παράδειγμα, αν δούμε ξανά την εικόνα <<r_pr_final>>, θα παρατηρήσουμε ότι ο συνεισφέρων δεν άλλαξε τη βάση της υποβολής του και έστειλε άλλο αίτημα έλξης. Αντίθετα, πρόσθεσε νέες υποβολές και τις ώθησε στον υφιστάμενο κλάδο. Με αυτόν τον τρόπο, αν επανέλθουμε και εξετάσουμε αυτό το αίτημα έλξης στο μέλλον, μπορούμε εύκολα να βρούμε όλο το πλαίσιο όσον αφορά στο γιατί λήφθησαν οι συγκεκριμένες αποφάσεις. Πατώντας το κουμπί ``Merge'' δημιουργεί σκόπιμα μια υποβολή συγχώνευσης που αναφέρεται στο αίτημα έλξης, έτσι ώστε να είναι εύκολο να επιστρέψουμε και να διερευνήσουμε την αρχική συνομιλία, εφόσον χρειαστεί κάτι τέτοιο.

===== Συμβαδίζοντας με το upstream

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

[[r_pr_fail]]
.Αποτυχία συγχώνευσης αιτήματος έλξης
image::images/pr-01-fail.png[Αποτυχία συγχώνευσης αιτήματος έλξης.]

Εάν δούμε κάτι σαν την εικόνα <<r_pr_fail>>, θα χρειαστεί να διορθώσουμε τον κλάδο μας έτσι ώστε ο κλάδος να γίνει πράσινος και ο διαχειριστής να μην χρειάζεται να κάνει επιπρόσθετη εργασία.

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

Οι περισσότεροι προγραμματιστές στο GitHub θα επιλέξουν να κάνουν το τελευταίο, για τους ίδιους λόγους που αναφέραμε στην προηγούμενη ενότητα. Αυτό που έχει σημασία είναι το ιστορικό και η τελική συγχώνευση, συνεπώς η αλλαγή βάσης (rebase), αν και είναι *πολύ* πιο δύσκολη και επιρρεπής σε σφάλματα, δεν μας προσφέρει τίποτα άλλο παρεκτός ένα ελαφρώς καθαρότερο ιστορικό.

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

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

[source,console]

$ git remote add upstream https://github.com/schacon/blink <1>

$ git fetch upstream <2> remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. Unpacking objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0) From https://github.com/schacon/blink * [new branch] master → upstream/master

$ git merge upstream/master <3> Auto-merging blink.ino CONFLICT (content): Merge conflict in blink.ino Automatic merge failed; fix conflicts and then commit the result.

$ vim blink.ino <4> $ git add blink.ino $ git commit [slow-blink 3c8d735] Merge remote-tracking branch upstream/master \ into slower-blink

$ git push origin slow-blink <5> Counting objects: 6, done. Delta compression using up to 8 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done. Total 6 (delta 2), reused 0 (delta 0) To https://github.com/tonychacon/blink ef4725c..3c8d735 slower-blink → slow-blink

<1> Προσθέτουμε το αρχικό αποθετήριο ως απομακρυσμένο με όνομα `upstream`
<2> Λαμβάνουμε τη νεότερη έκδοση του απομακρυσμένου αποθετηρίου
<3> Συγχωνεύουμε τον κύριο κλάδο στον θεματικό κλάδο
<4> Διορθώνουμε τη σύγκρουση που συνέβη
<5> Ωθούμε ξανά στον ίδιο θεματικό κλάδο.

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

[[r_pr_merge_fix]]
.Το αίτημα έλξης τώρα συγχωνεύεται χωρίς συγκρούσεις
image::images/pr-02-merge-fix.png[Το αίτημα έλξης τώρα συγχωνεύεται χωρίς συγκρούσεις]

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

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

===== Αναφορές

Η επόμενη ερώτησή μας μπορεί να είναι ``Πώς μπορώ να αναφερθώ στο παλιό αίτημα έλξης;'' Αποδεικνύεται ότι υπάρχουν πάρα πολλοί τρόποι να αναφερόμαστε σε άλλα πράγματα, σχεδόν οπουδήποτε μπορούμε να γράψουμε στο GitHub.

Ας ξεκινήσουμε με το πώς μπορούμε να αναφερθούμε σε κάποιο άλλο ζήτημα (issue) ή αίτημα έλξης. Σε όλα τα ζητήματα και τα αιτήματα έλξης έχουν εκχωρηθεί αριθμοί που είναι μοναδικοί στο πλαίσιο του έργου.
Για παράδειγμα, δεν μπορούμε να έχουμε Pull Request #3 και Issue #3.
Αν θέλουμε να αναφερθούμε σε οποιοδήποτε αίτημα έλξης ή ζήτημα από οποιοδήποτε άλλο, απλά να γράφουμε `#<αρθ>` σε οποιοδήποτε σχόλιο ή περιγραφή.
Μπορούμε επίσης να είμαστε πιο συγκεκριμένοι αν το αίτημα έλξης ή ή ζήτημα βρίσκεται κάπου αλλού· γράφουμε `username#<αρθ>` αν αναφερόμαστε σε ένα αίτημα έλξης ή ζήτημα σε μια διχάλα του αποθετηρίου στο οποίο βρισκόμαστε ή `username/repo#<αρθ>` για να αναφερθούμε σε κάτι που βρίσκεται σε άλλο αποθετήριο.

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

[[r_pr_references]]
.Αναφορές σε αιτήματα έλξης
image::images/mentions-01-syntax.png[Αναφορές σε αιτήματα έλξης.]

Όταν υποβάλουμε αυτό το αίτημα έλξης, θα δούμε όλες οι αναφορές να εμφανίζονται όπως στην εικόνα <<r_pr_references_render>>.

[[r_pr_references_render]]
.Εμφάνιση αναφορών σε αίτημα έλξης
image::images/mentions-02-render.png[Εμφάνιση αναφορών σε αίτημα έλξης.]

Παρατηρούμε ότι η πλήρης διεύθυνση URL του GitHub που βάλουμε εκεί συντομεύτηκε μόνο στις απαραίτητες πληροφορίες.

Τώρα, αν ο Tony επιστρέψει και κλείσει το αρχικό αίτημα έλξης, θα δούμε ότι επειδή το έχουμε αναφέρει στο νέο αίτημα, το GitHub δημιούργησε αυτόματα ένα συμβάν trackback στο χρονολόγιο του αιτήματος έλξης. Αυτό σημαίνει ότι όποιος επισκέπτεται αυτό το αίτημα έλξης και βλέπει ότι είναι κλειστό, μπορεί εύκολα να συνδεθεί με εκείνο το αίτημα έλξης που το αντικατέστησε. Ο σύνδεσμος θα μοιάζει με το <<r_pr_closed>>

[[r_pr_closed]]
.Εμφάνιση αναφορών σε κλειστό αίτημα έλξης.
image::images/mentions-03-closed.png[Εμφάνιση αναφορών σε κλειστό αίτημα έλξης.]

Εκτός από τον αριθμό έκδοσης, μπορούμε επίσης να αναφερθούμε σε μια συγκεκριμένη υποβολή με τον αριθμό SHA-1. Πρέπει να χρησιμοποιήσουμε και τους 40 χαρακτήρες του SHA-1, αλλά εάν το GitHub το δει σε ένα σχόλιο, θα συνδεθεί άμεσα με την υποβολή. Επαναλαμβάνουμε ότι μπορούμε να αναφερθούμε σε υποβολές σε διχάλες ή άλλα αποθετήρια με τον ίδιο τρόπο που κάναμε με τα ζητήματα.

==== Markdown

Η σύνδεση με άλλα θέματα είναι μόνο ένα από τα πολλά ενδιαφέροντα πράγματα που μπορούμε να κάνουμε με σχεδόν οποιοδήποτε πλαίσιο κειμένου στο GitHub. Στις περιγραφές των ζητημάτων και αιτημάτων έλξης, τα σχόλια, τα σχόλια κώδικα και πολλά άλλα, μπορούμε να χρησιμοποιήσουμε κάτι που ονομάζεται ``Markdown με άρωμα GitHub'' (GitHub flavored Markdown). Η Markdown είναι σαν να γράφουμε απλό κείμενο, το οποίο όμως εμφανίζεται με πλούσια μορφοποίηση.

Δείτε την εικόνα <<r_example_markdown>> για ένα παράδειγμα του πώς μπορούν να γραφτούν σχόλια ή κείμενο και στη συνέχεια να αποδοθούν χρησιμοποιώντας την Markdown.

[[r_example_markdown]]
.Παράδειγμα της Markdown: γραφή και εμφάνιση
image::images/markdown-01-example.png[Παράδειγμα της Markdown: γραφή και εμφάνιση]

===== Markdown με άρωμα GitHub

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

====== Λίστες καθηκόντων

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

Μπορούμε να δημιουργήσουμε μια λίστα εργασιών όπως αυτή:

[source]
  • [X] Write the code

  • ❏ Write all the tests

  • ❏ Document the code

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

[[r_task_lists]]
.Λίστα καθηκόντων όπως εμφανίζεται σε σχόλιο Markdown.
image::images/markdown-02-tasks.png[Παράδειγμα λίστας καθηκόντων.]

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

Επιπλέον το GitHub ψάχνει για λίστες καθηκόντων στα ζητήματά μας και τα αιτήματα έλξης και θα τα δείξει ως μεταδεδομένα στις σελίδες που τα παραθέτουν. Για παράδειγμα, εάν έχουμε ένα αίτημα έλξης με εργασίες και κοιτάζουμε τη σελίδα επισκόπησης όλων των αιτημάτων έλξης, μπορούμε να δούμε σε τι βαθμό έχουν υλοποιηθεί. Αυτό βοηθά στην ανάλυση των αιτημάτων έλξης σε υποκαθήκοντα και βοηθά στην παρακολούθηση της προόδου του κλάδου από όλους. Μπορούμε να δούμε ένα τέτοιο παράδειγμα στην εικόνα <<r_task_list_progress>>

[[r_task_list_progress]]
.περίληψη λίστας καθηκόντων σε αίτημα έλξης.
image::images/markdown-03-task-summary.png[Παράδειγμα λίστας καθηκόντων.]

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

====== Αποσπάσματα κώδικα

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

Για να προσθέσουμε ένα κομμάτι κώδικα, πρέπει να το περικλείσουμε σε βαρείες.

[source]
for(int i=0 ; i < 5 ; i++)
{
   System.out.println("i is : " + i);
}
Εάν προσθέσουμε ένα όνομα γλώσσας όπως κάναμε εκεί με τη λέξη 'java', το GitHub θα προσπαθήσει επίσης να επισημάνει συντακτικά το απόσπασμα. Στην περίπτωση του παραπάνω παραδείγματος, θα καταλήξει σαν την εικόνα <<r_md_code>>

[[r_md_code]]
.Παράδειγμα απόδοσης κώδικα περικλεισμένου σε βαρείες.
image::images/markdown-04-fenced-code.png[Παράδειγμα απόδοσης κώδικα περικλεισμένου σε βαρείες.]

====== Παράθεμα

Εάν απαντάμε σε ένα μικρό κομμάτι ενός μακροσκελούς σχολίου, μπορούμε να κάνουμε επιλεκτική παράθεση από το άλλο σχόλιο ξεκινώντας τις γραμμές με τον χαρακτήρα `>`. Στην πραγματικότητα, αυτό είναι τόσο σύνηθες και τόσο χρήσιμο ώστε υπάρχει συντόμευση πληκτρολογίου για αυτό. Εάν επιλέξουμε κείμενο σε ένα σχόλιο στο οποίο θέλουμε να απαντήσουμε άμεσα και πατήσουμε το πλήκτρο `r`, θα παρατεθεί αυτό το κείμενο στο πλαίσιο σχολίων.

Τα παραθέματα μοιάζουν με το παρακάτω:

[source]

Whether 'tis Nobler in the mind to suffer The Slings and Arrows of outrageous Fortune,

How big are these slings and in particular, these arrows?

Μόλις αποδοθεί, το σχόλιο θα μοιάζει με την εικόνα <<r_md_quote>>

[[r_md_quote]]
.Παράδειγμα απόδοσης παραθέματος.
image::images/markdown-05-quote.png[Απόδοση παραθέματος.]

====== Emoji

Τέλος, στα σχόλιά μας μπορούμε επίσης να χρησιμοποιήσουμε emoji. Τα emoji χρησιμοποιούνται πραγματικά πολύ εκτενώς στα σχόλια που βλέπουμε σε πολλά ζητήματα και αιτήματα έλξης στο GitHub. Μάλιστα, υπάρχει ένας βοηθός emoji στο GitHub. Αν πληκτρολογούμε ένα σχόλιο και ξεκινάμε με ένα χαρακτήρα `:`, μία λίστα αυτόματης συμπλήρωσης θα μας βοηθήσει να βρούμε αυτό που ψάχνουμε.

[[r_md_emoji_auto]]
.Λίστα αυτόματης συμπλήρωσης emoji.
image::images/markdown-06-emoji-complete.png[Λίστα αυτόματης συμπλήρωσης emoji]

Τα emoji έχουν τη μορφή `:<όνομα>:` οπουδήποτε στο σχόλιο. Για παράδειγμα, θα μπορούσαμε να γράψουμε κάτι σαν αυτό:

[source]

I :eyes: that :bug: and I :cold_sweat:.

:+1: and :sparkles: on this :ship:, it’s :fire::poop:!

:clap::tada::panda_face:

Αυτό θα εμφανιστεί όπως στην εικόνα << _md_emoji>>


[[r_md_emoji]]
.Σχολιασμός με πολλά emoji.
image::images/markdown-07-emoji.png[Emoji.]

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

[NOTE]
====
Στην πραγματικότητα υπάρχουν αρκετές διαδικτυακές υπηρεσίες που κάνουν χρήση των χαρακτήρων emoji σήμερα. Ένα εξαιρετικό σκονάκι για emoji υπάρχει στο:

http://www.emoji-cheat-sheet.com[]
====

====== Εικόνες

Το παρακάτω στην πραγματικότητα δεν είναι Markdown με άρωμα GitHub, αλλά είναι εξαιρετικά χρήσιμο. Η προσθήκη συνδέσμων σε εικόνες μπορεί να είναι μπελάς, αφού η εύρεση και ενσωμάτωση των URL των εικόνων μπορεί να είναι δύσκολη. Γι' αυτό, το GitHub μάς επιτρέπει να μεταφέρουμε και να ενσωματώσουμε εικόνες σε περιοχές κειμένου με μεταφορά-και-απόθεση.

[[r_md_drag]]
.Μεταφορά-και-απόθεση εικόνων για μεταφόρτωσή και αυτόματη ενσωμάτωσή τους
image::images/markdown-08-drag-drop.png[Μεταφορά-και-απόθεση εικόνων.]

Αν ανατρέξουμε στην εικόνα <<r_pr_references>>, μπορούμε να δούμε μια μικρή υπόδειξη `Parsed as Markdown` πάνω από την περιοχή κειμένου. Κάνοντας κλικ σε αυτό θα μας δοθεί ένα πλήρες σκονάκι με ό,τι μπορούμε να κάνουμε με την Markdown στο GitHub.


[[r_maintaining_gh_project]]
=== Συντήρηση ενός έργου

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

==== Δημιουργία νέου αποθετηρίου

Ας δημιουργήσουμε ένα νέο αποθετήριο για να μοιραστούμε τον κώδικα του έργου μας.
Ξεκινάμε κάνοντας κλικ στο κουμπί ``New repository'' στη δεξιά πλευρά του ταμπλό ή στο κουμπί ``+'' στην επάνω γραμμή εργαλείων δίπλα στο όνομα χρήστη μας όπως φαίνεται στην εικόνα <<r_new_repo_dropdown>>.

.Η περιοχή ``Your repositories''
image::images/newrepo.png[Η περιοχή ``Your repositories''.]

[[r_new_repo_dropdown]]
.Η αναπτυσσόμενη λίστα ``New repository''.
image::images/new-repo.png[Η αναπτυσσόμενη λίστα ``New repository''.]

Αυτό μας μεταφέρει στη φόρμα ``Νew repository'':

.Η φόρμα ``New repository''.
image::images/newrepoform.png[Η φόρμα ``New repository''.]

Το μόνο που έχουμε να κάνουμε εδώ είναι να δώσουμε ένα όνομα έργου. Τα υπόλοιπα πεδία είναι εντελώς προαιρετικά.
Προς το παρόν, απλά κάνουμε κλικ στο κουμπί ``Create Repository'' και αμέσως έχουμε ένα νέο αποθετήριο στο GitHub, το οποίο ονομάζεται `<χρήστης>/<όνομα_έργου>`.

Εφόσον δεν έχουμε ακόμα κανένα κώδικα, το GitHub θα μας δείξει οδηγίες για τον τρόπο δημιουργίας ενός ολοκαίνουργιου αποθετηρίου Git ή τη σύνδεση ενός υπάρχοντος έργου Git.
Δεν θα εντρυφήσουμε εδώ· τα σχετικά υπάρχουν στο κεφάλαιο <<ch02-git-basics>>.

Τώρα που το έργο μας φιλοξενείται στο GitHub, μπορούμε να δώσουμε τη διεύθυνση URL σε οποιονδήποτε θέλουμε να μοιραστούμε το έργο μας.
Κάθε έργο στο GitHub είναι προσβάσιμο μέσω HTTP στη διεύθυνση `https://github.com/<χρήστης>/<όνομα_έργου>`, και μέσω SSH ως `git@github.com:<χρήστης>/<όνομα_έργου>`.
Το Git μπορεί να ανακτήσει από και να ωθήσει προς και τις δύο αυτές διευθύνσεις URL, αλλά η πρόσβαση ταυτοποιείται με βάση τα διαπιστευτήρια του χρήστη που συνδέεται σε αυτές.

[NOTE]
====
Συχνά είναι προτιμότερο να μοιραζόμαστε τη διεύθυνση URL μέσω HTTP για ένα δημόσιο έργο, καθώς ο χρήστης δεν χρειάζεται να έχει λογαριασμό στο GitHub ώστε να έχει πρόσβαση σε αυτόν για κλωνοποίηση.
Οι χρήστες θα πρέπει να έχουν λογαριασμό και μεταφορτωμένο κλειδί SSH για να αποκτήσουν πρόσβαση στο έργο μας, εφόσον τους δώσουμε τη διεύθυνση URL μέσω SSH.
Το HTTP είναι ακριβώς το ίδιο URL με αυτό που θα επικολλούσε κανείς σε ένα πρόγραμμα περιήγησης για να δει το έργο σε αυτό.
====

==== Προσθήκη συνεργατών

Εάν εργαζόμαστε με άλλα άτομα στα οποία θέλουμε να επιτρέψουμε την πρόσβαση, πρέπει να τα προσθέσουμε ως ``συνεργάτες'' (collaborators).
Αν ο Ben, ο Jeff και η Louise έχουν λογαριασμούς στο GitHub και θέλουμε να τους δώσουμε δικαίωμα ώθησης στο αποθετήριό μας, μπορούμε να τους προσθέσουμε στο έργο μας.
Κάτι τέτοιο θα τους δώσει πρόσβαση ώθησης, που σημαίνει ότι έχουν δικαίωμα τόσο ανάγνωσης όσο και εγγραφής στο έργο και στο αποθετήριο Git.

Κάνουμε κλικ στον σύνδεσμο ``Settings'' στο κάτω μέρος της δεξιάς πλευρικής μπάρας.

.Ο σύνδεσμος ``Settings'' του αποθετηρίου.
image::images/reposettingslink.png[Ο σύνδεσμος ``Settings'' του αποθετηρίου.]

Στη συνέχεια επιλέγουμε ``Collaborators'' από το μενού στα αριστερά.
Μετά πληκτρολογούμε ένα όνομα χρήστη στο πλαίσιο και κάνουμε κλικ στο κουμπί ``Add collaborator''.
Μπορούμε να επαναλάβουμε αυτήν τη διαδικασία όσες φορές θέλουμε ώστε να δώσουμε πρόσβαση σε όποιον θέλουμε.
Αν χρειαστεί να ανακαλέσουμε την πρόσβαση κάποιου χρήστη, απλά κάνουμεε κλικ στο ``×'' στα δεξιά της σειράς του.

.Το πλαίσιο με τους συνεργάτες του αποθετηρίου.
image::images/collaborators.png[Το πλαίσιο με τους συνεργάτες του αποθετηρίου.]

==== Διαχείριση αιτημάτων έλξης

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

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

Για αυτά τα παραδείγματα, ας υποθέσουμε ότι είμαστε ο `tonychacon` και έχουμε δημιουργήσει ένα νέο έργο με κώδικα Arduino που ονομάζεται `fade`.

[[r_email_notifications]]
===== Ειδοποιήσεις e-mail

Κάποιος κάνει μια αλλαγή στον κώδικά μας και μας στέλνει ένα αίτημα έλξης.
Θα πρέπει να λάβουμε ένα μήνυμα e-mail που μας ειδοποιεί για το νέο αίτημα έλξης που θα μοιάζει σαν αυτό της εικόνας <<r_email_pr>>

[[r_email_pr]]
.Ειδοποίηση email για νέο αίτημα έλξης.
image::images/maint-01-email.png[Ειδοποίηση email για νέο αίτημα έλξης.]

Σε αυτό το μήνυμα e-mail παρατηρούμε τα εξής.
Μας δίνει ένα μικρό diffstat —μια λίστα των αρχείων που έχουν αλλάξει στο αίτημα έλξης και κατά πόσο έχουν αλλάξει.
Mας δίνει ένα σύνδεσμο προς το αίτημα έλξης στο GitHub.
Mας δίνει επίσης μερικές διευθύνσεις URL που μπορούμε να χρησιμοποιήσουμε από τη γραμμή εντολών.

Η γραμμή που λέει `git pull <url> patch-1`, είναι ένας απλός τρόπος για να συγχωνεύσουμε έναν απομακρυσμένο κλάδο χωρίς να χρειάζεται να προσθέσουμε ένα απομακρυσμένο αποθετήριο.
Αυτό το είδαμε αυτό εν συντομία στην ενότητα <<r_checking_out_remotes>>.
Αν θέλουμε, μπορούμε να δημιουργήσουμε έναν θεματικό κλάδο, να μεταβούμε σε αυτόν και στη συνέχεια να εκτελέσουμε αυτήν την εντολή για να συγχωνεύσουμε τις αλλαγές του αιτήματος έλξης.

Οι άλλες ενδιαφέρουσες διευθύνσεις URL είναι οι διευθύνσεις `.diff` και `.patch`, οι οποίες, όπως μπορεί να μαντέψει κανείς, παρέχουν ενοποιημένες εκδόσεις της diff και του επιθέματος του αιτήματος έλξης.
Θα μπορούσαμε να συγχωνεύσουμε την εργασία του αιτήματος έλξης με κάτι σαν αυτό:

[source,console]
===== Συνεργασία σε αίτημα έλξης

Όπως είδαμε στην ενότητα <<r_github_flow>>, μπορούμε να έχουμε μια συνομιλία με το άτομο που υπέβαλε το αίτημα έλξης.
Μπορούμε να σχολιάζουμε συγκεκριμένες γραμμές κώδικα, ολόκληρες υποβολές ακόμα και ολόκληρο το ίδιο το αίτημα έλξης, χρησιμοποιώντας τη Markdown με άρωμα GitHub.

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

.Οι απαντήσεις στα e-mails περιλαμβάνονται στο νήμα συζήτησης.
image::images/maint-03-email-resp.png[Απάντηση e-mail.]

Μόλις ο κώδικας βρίσκεται σε μία κατάσταση που μας αρέσει και θέλουμε να τον συγχωνεύσουμε, μπορούμε είτε να έλξουμε τον κώδικα και να τον συγχωνεύσουμε τοπικά, είτε με τη σύνταξη `git pull <url> <κλάδος>` που είδαμε προηγουμένως, είτε προσθέτοντας τη διχάλα ως απομακρυσμένο αποθετήριο και ανακτώντας και συγχωνεύοντας.

Εάν η συγχώνευση είναι τετριμμένη, μπορούμε επίσης να πατήσουμε το κουμπί ``Merge'' στην τοποθεσία GitHub.
Αυτό θα κάνει μια συγχώνευση ``μη-ταχυπροώθησης'', δημιουργώντας μια υποβολή συγχώνευσης ακόμα και αν ήταν δυνατή μια συγχώνευση ταχυπροώθησης.
Αυτό σημαίνει ότι όπως και νά 'χει, κάθε φορά που πατάμε το κουμπί συγχώνευσης, δημιουργείται μια υποβολή συγχώνευσης.
Όπως μπορούμε να δούμε στην εικόνα <<r_merge_button>>, το GitHub μας δίνει όλες αυτές τις πληροφορίες εάν κάνουμε κλικ στον σύνδεσμο ``hint''.

[[r_merge_button]]
.Κουμπί ``Merge'' και οδηγίες για συγχώνευση αιτήματος έλξης
image::images/maint-02-merge.png[Κουμπί ``Merge'' και οδηγίες για συγχώνευση αιτήματος έλξης]

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

[[r_pr_refs]]
===== Refs αιτημάτων έλξης

Εάν έχουμε να κάνουμε με *πολλά* αιτήματα έλξης και δεν θέλουμε να προσθέσουμε πολλά απομακρυσμένα αποθετήρια ή να κάνουμε μία έλξη κάθε φορά, υπάρχει ένα ωραίο κόλπο που μας επιτρέπει να κάνουμε το GitHub.
Είναι λίγο προηγμένο τέχνασμα και θα δούμε τις λεπτομέρειές του σε μεγαλύτερο βάθος στην ενότητα <<r_refspec>>, αλλά μπορεί να είναι αρκετά χρήσιμο.

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

Για να το δείξουμε αυτό, θα χρησιμοποιήσουμε μια εντολή χαμηλού επιπέδου (που συχνά αναφέρεται ως εντολή ``διοχέτευσης'' (plumbing), την `ls-remote`, για την οποία θα πούμε περισσότερα στην ενότητα <<r_plumbing_porcelain>>).
Αυτή η εντολή γενικά δεν χρησιμοποιείται στις καθημερινές λειτουργίες του Git, αλλά μας χρησιμεύει να δούμε ποιες αναφορές υπάρχουν στον διακομιστή.

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

[source,console]

$ git ls-remote https://github.com/schacon/blink 10d539600d86723087810ec636870a504f4fee4d HEAD 10d539600d86723087810ec636870a504f4fee4d refs/heads/master 6a83107c62950be9453aac297bb0193fd743cd6e refs/pull/1/head afe83c2d1a70674c9505cc1d8b7d380d5e076ed3 refs/pull/1/merge 3c8d735ee16296c242be7a9742ebfbc2665adec1 refs/pull/2/head 15c9f4f80973a2758462ab2066b6ad9fe8dcf03d refs/pull/2/merge a5a7751a33b7e86c5e9bb07b26001bb17d775d1a refs/pull/4/head 31a45fc257e8433c8d8804e3e848cf61c9d3166c refs/pull/4/merge

Φυσικά, εάν βρισκόμαστε στο δικό μας αποθετήριο και εκτελέσουμε `git ls-remote origin` ή οποιοδήποτε απομακρυσμένο αποθετήριο θέλουμε να ελέγξουμε, θα μας δείξει κάτι παρόμοιο με αυτό.

Αν το αποθετήριο βρίσκεται στο GitHub και έχουμε υποβεβλημένα αίτηματα έλξης, θα λάβουμε αυτές τις αναφορές, με πρόθεμα `refs/pull/`.
Αυτές είναι ουσιαστικά κλάδοι, αλλά επειδή δεν βρίσκονται στον `refs/heads/`, δεν τις παίρνουμε όταν κλωνοποιούμε ή ανακτούμε από τον διακομιστή —κάτω από κανονικές συνθήκες η διαδικασία της ανάκτησης τούς αγνοεί.

Υπάρχουν δύο αναφορές ανά αίτημα έλξης —αυτή που τελειώνει σε `/head` δείχνει στην ίδια ακριβώς υποβολή με την τελευταία υποβολή στον κλάδο του αιτήματος έλξης.
Έτσι, αν κάποιος υποβάλει ένα αίτημα έλξης στο αποθετήριό μας και ο κλάδος του ονομάζεται `bug-fix` και δείχνει στην υποβολή `a5a775`, τότε στο *δικό μας* αποθετήριο δεν θα έχουμε κλάδο `bug-fix ' (αφού αυτός βρίσκεται στη δική του διχάλα), αλλά θα έχουμε `pull/<αε#>/head` που δείχνει στην `a5a775`.
Αυτό σημαίνει ότι μπορούμε πολύ εύκολα να έλξουμε κάθε κλάδο ενός αιτήματος έλξης χωρίς να χρειαστεί να προσθέσουμε κάμποσα απομακρυσμένα αποθετήρια.

Τώρα, μπορούμε να ανακτήσουμε απευθείας την αναφορά.

[source,console]

$ git fetch origin refs/pull/958/head From https://github.com/libgit2/libgit2 * branch refs/pull/958/head → FETCH_HEAD

Αυτό λέει στο Git, ``συνδέσου στο απομακρυσμένο αποθετήριο `origin` και κατέβασε το ref με όνομα `refs/pull/958/head`''.
Το Git υπακούει και κατεβάζει ό,τι χρειαζόμαστε για να κατασκευάσουμε αυτό το ref και βάζει έναν δείκτη στην υποβολή που θέλουμε στο αρχείο `.git/FETCH_HEAD`.
Μπορούμε να τη συγχωνεύσουμε με την εντολή `git merge FETCH_HEAD` σε έναν κλάδο στον οποίο θέλουμε να το δοκιμάσουμε, αλλά αυτό το μήνυμα συγχώνευσης φαίνεται λίγο παράξενο.
Επίσης, εάν εξετάζουμε *πολλά* αιτήματα έλξης, κάτι τέτοιο γίνεται κουραστικό.

Υπάρχει επίσης ένας τρόπος για να ανακτήσουμε _όλα_ τα αιτήματα έλξης και να τα κρατάμε ενημερωμένα κάθε φορά που συνδεόμαστε στο απομακρυσμένο αποθετήριο.
Ανοίγουμε το `.git/config` στον αγαπημένο μας επεξεργαστή κειμένου και αναζητούμε το απομακρυσμένο αποθετήριο `origin`.
Θα πρέπει να μοιάζει κάπως έτσι:
url = https://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/origin/*
Αυτή η γραμμή που αρχίζει με `fetch =` είναι ένα ``refspec''.
Είναι ένας τρόπος απεικόνισης ονομάτων του απομακρυσμένου αποθετηρίου με ονόματα στον τοπικό μας κατάλογο `.git`.
Αυτό το συγκεκριμένο λέει στο Git, ``τα πράγματα στο απομακρυσμένο αποθετήριο που βρίσκονται κάτω από το `refs/heads` θα πρέπει να πάνε στο τοπικό μου αποθετήριο κάτω από το `refs/remotes/origin`.''
Μπορούμε να τροποποιήσουμε αυτό το τμήμα ώστε να προσθέσουμε ένα ακόμα refspec:
url = https://github.com/libgit2/libgit2.git
fetch = +refs/heads/*:refs/remotes/origin/*
fetch = +refs/pull/*/head:refs/remotes/origin/pr/*
Αυτή η τελευταία γραμμή λέει στο Git, ``Όλα τα refs που μοιάζουν με το `refs/pull/123/head` θα πρέπει να αποθηκεύονται τοπικά όπως το `refs/remotes/origin/pr/123`''.
Τώρα αν αποθηκεύσουμε αυτό το αρχείο και εκτελέσουμε την `git fetch`, παίρνουμε:

[source,console]

$ git fetch # … * [new ref] refs/pull/1/head → origin/pr/1 * [new ref] refs/pull/2/head → origin/pr/2 * [new ref] refs/pull/4/head → origin/pr/4 # …

Τώρα όλα τα απομακρυσμένα αιτήματα έλξης αναπαριστώνται τοπικά με αναφορές που λειτουργούν σαν παρακολουθούμενοι κλάδων· είναι μόνο για ανάγνωση και ενημερώνονται όταν κάνουμε `fetch`.
Αυτό καθιστά πανεύκολο το να δοκιμάσουμε τον κώδικα από ένα αίτημα έλξης σε τοπικό επίπεδο:

[source,console]

$ git checkout pr/2 Checking out files: 100% (3769/3769), done. Branch pr/2 set up to track remote branch pr/2 from origin. Switched to a new branch pr/2

Αν είστει εξαιρετικά παρατηρητικοί, θα έχετε εντοπίσει το `head` στο τέλος του απομακρυσμένου τμήματος του refspec.
Υπάρχει επίσης ένας σύνδεσμος `refs/pull/#/merge` στην πλευρά του GitHub, που αντιπροσωπεύει την υποβολή που θα προκύψει αν πατήσουμε το κουμπί ``Merge''.
Αυτό μπορεί να μας επιτρέψει να δοκιμάσουμε τη συγχώνευση ακόμη και πριν πατήσουμε το κουμπί.

===== Αιτήματα έλξης σε αιτήματα έλξης

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

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

Όταν πάμε να υποβάλουμε ένα αίτημα έλξης, υπάρχει ένα πλαίσιο στο επάνω μέρος της σελίδας που καθορίζει από ποιον και προς ποιον κλάδο αιτούμαστε να έλξουμε.
Αν πατήσουμε το κουμπί ``Edit'' στα δεξιά του πλαισίου μπορούμε να αλλάξουμε όχι μόνο τους κλάδους αλλά και τη διχάλα.

[[r_pr_targets]]
.Χειροκίνητη αλλαγή της διχάλας και κλάδου σε αίτημα έλξης.
image::images/maint-04-target.png[Χειροκίνητη αλλαγή της διχάλας και κλάδου σε αίτημα έλξης.]

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

==== Μνείες και ειδοποιήσεις

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

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

.Πληκτρολογούμε `@` για να μνημονεύσουμε κάποιον.
image::images/maint-05-mentions.png[Μνείες]

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

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

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

.Διαγραφή από λίστα ενημερώσεων για ένα ζήτημα ή αίτημα έλξης.
image::images/maint-06-unsubscribe.png[Διαγραφή από λίστα ενημερώσεων για ζήτημα ή αίτημα έλξης.]

===== Η σελίδα των ειδοποιήσεων

Όταν λέμε ``ειδοποιήσεις'' εδώ σε σχέση με το GitHub, εννοούμε έναν συγκεκριμένο τρόπο με τον οποίο το GitHub προσπαθεί να έρθει σε επαφή μαζί μας όταν συμβαίνουν κάτι και υπάρχουν διάφοροι τρόποι με τους οποίους μπορούμε να τις διαμορφώσουμε.
Αν μεταβούμε στην καρτέλα ``Notification center'' από τη σελίδα ρυθμίσεων, μπορούμε να δούμε μερικές από τις επιλογές που έχουμε.

.Επιλογές κέντρου ειδοποιήσεων.
image::images/maint-07-notifications.png[Επιλογές κέντρου ειδοποιήσεων.]

Οι δύο επιλογές είναι να λαμβάνουμε ειδοποιήσεις σχετικά μέσω ``Email'' και μέσω ``Web'' και μπορούμε να επιλέξουμε είτε μία από τις δύο, καμία από τις δύο ή και τις δύο όταν συμμετέχουμε ενεργά σε ό,τι συμβαίνει ή για δραστηριότητες στα αποθετήρια που παρακολουθούμε.

====== Ειδοποιήσεις μέσω web

Οι ειδοποιήσεις ιστού υπάρχουν μόνο στο GitHub και μπορούμε να τις δούμε μόνο στο GitHub.
Εάν έχουμε επιλέξει αυτήν την επιλογή στις προτιμήσεις μας και ενεργοποιηθεί μια ειδοποίηση για μας, θα δούμε μια μικρή μπλε κουκίδα πάνω από το εικονίδιο ειδοποιήσεων μας στο επάνω μέρος της οθόνης, όπως φαίνεται στην εικόνα <<r_not_center>>

[[r_not_center]]
.Κέντρο ειδοποιήσεων.
image::images/maint-08-notifications-page.png[Κέντρο ειδοποιήσεων.]

Εάν κάνουμε κλικ σ' αυτό, θα δούμε μια λίστα με όλα τα στοιχεία για τα οποία έχουμε ειδοποιηθεί, ομαδοποιημένα κατά έργο.
Μπορούμε να φιλτράρουμε τις ειδοποιήσεις ενός συγκεκριμένου έργου κάνοντας κλικ στο όνομά του στην αριστερή πλευρική μπάρα.
Μπορούμε επίσης να επιβεβαιώσουμε τη λήψη της ειδοποίησης κάνοντας κλικ στο εικονίδιο tick δίπλα σε οποιαδήποτε ειδοποίηση ή να επιβεβαιώσουμε τη λήψη _όλων_ των ειδοποιήσεων σε ένα έργο κάνοντας κλικ στο εικονίδιο tick στο πάνω μέρος της ομάδας.
Υπάρχει επίσης ένα κουμπί σίγασης δίπλα σε κάθε σημάδι επιλογής στο οποίο μπορούμε να κάνουμε κλικ για να μην λαμβάνουμε άλλες ειδοποιήσεις σχετικά με το συγκεκριμένο στοιχείο.

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

====== Ειδοποιήσεις μέσω e-mail

Οι ειδοποιήσεις μέσω e-mail είναι ο άλλος τρόπος με τον οποίο μπορούμε να χειριστούμε τις ειδοποιήσεις μέσα από το GitHub.
Εάν έχουμε ενεργοποιήσει αυτήν τη λειτουργία, θα λαμβάνουμε μηνύματα e-mail για κάθε ειδοποίηση.
Είδαμε παραδείγματα αυτής της λειτουργίας στην ενότητα <<r_email_notification>> και την εικόνα <<r_email_pr>>.
Τα μηνύματα e-mail επίσης θα ταξινομούντα κατά νήμα, κάτι πολύ χρήσιμο εφόσον χρησιμοποιούμε ένα πρόγραμμα e-mail που υποστηρίζει τα νήματα.

Υπάρχει επίσης μία σημαντική ποσότητα μεταδεδομένων ενσωματωμένων στις κεφαλίδες των μηνυμάτων e-mail που μας στέλνει το GitHub, κάτι που μπορεί να είναι πραγματικά χρήσιμο για τη δημιουργία προσαρμοσμένων φίλτρων και κανόνων.

Για παράδειγμα, αν κοιτάξουμε τις πραγματικές κεφαλίδες e-mail που αποστέλλονται στον Tony στο e-mail που εμφανίζεται στην εικόνα <<r_email_pr>>, θα δούμε τα παρακάτω μεταξύ των πληροφοριών που στάλθηκαν:

[source,mbox]

To: tonychacon/fade <fade@noreply.github.com> Message-ID: <tonychacon/fade/pull/1@github.com> Subject: [fade] Wait longer to see the dimming effect better (#1) X-GitHub-Recipient: tonychacon List-ID: tonychacon/fade <fade.tonychacon.github.com> List-Archive: https://github.com/tonychacon/fade List-Post: <mailto:reply+i-4XXX@reply.github.com> List-Unsubscribe: <mailto:unsub+i-XXX@reply.github.com>,…​ X-GitHub-Recipient-Address: tchacon@example.com

Υπάρχουν μερικά ενδιαφέροντα πραγματάκια εδώ.
Εάν θέλουμε να επισημάνουμε ή να επαναπροωθήσουμε τα μηνύματα e-mail σε αυτό το συγκεκριμένο έργο ή ακόμα και το αίτημα έλξης, οι πληροφορίες στο "Message-ID" μάς παρέχουν όλα τα δεδομένα στη μορφή `<χρήστης>/<έργο>/<τύπος>`.
Αν αυτό ήταν ένα ζήτημα, για παράδειγμα, το πεδίο `<τύπος>` θα έλεγε `issues` αντί για `pull`.

Τα πεδία `List-Post` και` List-Unsubscribe` σημαίνουν ότι αν έχουμε ένα πρόγραμμα e-mail που τα καταλαβαίνει, μπορούμε εύκολα να αναρτήσουμε στη λίστα ή να διαγραφούμε από το νήμα.
Αυτό θα ήταν ουσιαστικά το ίδιο με το κλικ στο κουμπί ``Mute'' στη διαδικτυακή μορφή της ειδοποίησης ή ``Unsubscribe'' στη σελίδα ζητημάτων ή αιτημάτων έλξης.

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

==== Ειδικά αρχεία

Υπάρχουν μερικά ειδικά αρχεία τα οποία θα παρατηρήσει το GitHub εάν υπάρχουν στο αποθετήριό μας.

==== Αρχείο README

Το πρώτο είναι το αρχείο `README`, το οποίο μπορεί να είναι σχεδόν κάθε μορφής που αναγνωρίζει το GitHub ως κείμενο.
Για παράδειγμα, θα μπορούσε να είναι `README`, `README.md`, `README.asciidoc`, κ.λπ.
Αν το GitHub δει ένα αρχείο `README`, θα εμφανίσει στην αρχική σελίδα του έργου.

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

* Σε τι αφορά το έργο
* Πώς να το ρυθμίσουμε και εγκαταστήσουμε
* Ένα παράδειγμα για το πώς να το χρησιμοποιήσουμε ή να το τρέξουμε
* Η άδεια χρήσης του έργου
* Πώς να συμβάλλουμε σε αυτό

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

==== Αρχείο CONTRIBUTING

Το άλλο ειδικό αρχείο που αναγνωρίζει το GitHub είναι το αρχείο `CONTRIBUTING`.
Εάν έχουμε ένα αρχείο που ονομάζεται `CONTRIBUTING` με οποιαδήποτε κατάληξη αρχείου, το GitHub θα εμφανίσει την εικόνα <<r_contrib_file>>, όταν κάποιος ξεκινά την υποβολή ενός αιτήματος έλξης.

[[r_contrib_file]]
.Υποβολή αιτήματος έλξης όταν υπάρχει αρχείο `CONTRIBUTING`
image::images/maint-09-contrib.png[Ειδοποίηση συνεισφοράς.]

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

==== Διαχείριση έργων

Γενικά, δεν υπάρχουν πολλά διαχειριστικά πράγματα που μπορούμε να κάνουμε με ένα μόνο έργο, αλλά υπάρχουν μερικά στοιχεία που μπορεί να ενδιαφέρουν.

===== Αλλαγή του προεπιλεγμένου κλάδου

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

[[r_default_branch]]
.Αλλαγή του προεπιλεγμένου κλάδου για ένα έργο.
image::images/maint-10-default-branch.png[Προεπιλεγμένος κλάδος.]

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

===== Μεταβίβαση έργου

Εάν θέλουμε να μεταβιβάσουμε ένα έργο σε άλλον χρήστη ή οργανισμό στο GitHub, υπάρχει μια επιλογή ``Transfer ownership'' στο κάτω μέρος της καρτέλας ``Options'' της σελίδας ρυθμίσεων του αποθετηρίου που μας επιτρέπει να το κάνουμε.

[[r_transfer_project]]
.Μεταβίβαση έργου σε άλλον χρήστη ή οργάνωση του GitHub.
image::images/maint-11-transfer.png[Μεταβίβαση έργου.]

Αυτό είναι χρήσιμο εάν εγκαταλείπουμε ένα έργο και κάποιος θέλει να το αναλάβει ή εάν το έργο μας μεγαλώνει και θέλουμε να το μεταβιβάσουμε σε κάποιον οργανισμό.

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


[[r_github_orgs]]
=== Διαχείριση οργανισμώνν

(((GitHub, organizations)))
Εκτός από λογαριασμούς ενός χρήστη, το GitHub έχει κάτι που ονομάζεται _οργανισμός_ (organizations).
Όπως οι προσωπικοί λογαριασμοί, οι λογαριασμοί οργανισμών έχουν έναν ονοματοχώρο όπου υπάρχουν όλα τα έργα τους, όμως διαφέρουν σε πολλά άλλα πράγματα.
Αυτοί οι λογαριασμοί αντιπροσωπεύουν μια ομάδα ατόμων με κοινή ιδιοκτησία έργων και υπάρχουν πολλά εργαλεία για τη διαχείριση υποομάδων αυτών των ατόμων.
Συνήθως αυτοί οι λογαριασμοί χρησιμοποιούνται για ομάδες ανοιχτού κώδικα (π.χ. `perl` ή `rails`) ή εταιρείες (π.χ. `google` ή `twitter`).

==== Βασικά στοιχεία οργανισμών

Είναι πολύ εύκολο να δημιουργηθεί ένας οργανισμός· απλά κάνουμε κλικ στο εικονίδιο ``+'' στην πάνω δεξιά γωνία οποιασδήποτε σελίδας του GitHub και επιλέγουμε ``New organization'' (``Νέος οργανισμός'') από το μενού.

.Το στοιχείο μενού ``New organization''.
image::images/neworg.png[Το στοιχείο μενού ``New organization''.]

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

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

Ως ιδιοκτήτης ενός οργανισμού, όταν αποσχίζουμε ένα απόθετήριο από ένα άλλο, θα έχουμε την επιλογή να το αποσχίσουμε στον ονοματοχώρο του οργανισμού μας.
Όταν δημιουργούμε νέα αποθετήρια, μπορούμε να τα δημιουργήσουμε είτε στον προσωπικό μας λογαριασμό είτε σε οποιονδήποτε οργανισμό είμαστε ιδιοκτήτες.
Μπορούμε επίσης αυτόματα να ``παρακολουθούμε'' κάθε νέο αποθετήριο που δημιουργήθηκε κάτω από αυτούς τους οργανισμούς.

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

Τώρα θα καλύψουμε μερικά από τα πράγματα που είναι λίγο διαφορετικά σε έναν λογαριασμό οργανισμού από ότι σε προσωπικούς λογαριασμούς.

==== Ομάδες

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

Για παράδειγμα, ας πούμε ότι η εταιρεία μας διαθέτει τρία αποθετήρια: `frontend`, `backend` και `deployscripts`.
Θα θέλαμε οι προγραμματιστές HTML/CSS/Javascript να έχουν πρόσβαση στο `frontend` και ίσως στο `backend` και οι επιχειρησιακοί να έχουν πρόσβαση στα `backend` και `deployscripts`.
Οι ομάδες το καθιστούν αυτό εύκολο, χωρίς να χρειάζεται να διαχειριζόμαστε τους συνεργάτες για κάθε μεμονωμένο αποθετήριο.

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

[[r_org_page]]
.Η σελίδα του οργανισμού
image::images/orgs-01-page.png[Η σελίδα του οργανισμού.]

Για να διαχειριστούμε τις ομάδες μας, μπορούμε να κάνουμε κλικ στην πλευρική μπάρα ``Teams'' στο δεξί μέρος της σελίδας στην εικόνα <<r_org_page>>
Αυτό θα μας φέρει σε μια σελίδα που μπορούμε να χρησιμοποιήσουμε για να προσθέσουμε μέλη στην ομάδα, να προσθέσουμε αποθετήρια στην ομάδα ή να διαχειριστούμε τις ρυθμίσεις και τα επίπεδα ελέγχου πρόσβασης για την ομάδα.
Κάθε ομάδα μπορεί να έχει πρόσβαση μόνο-για-ανάγνωση, ανάγνωση/εγγραφή ή και δικαιώματα administrator στα αποθετήρια.
Μπορούμε να αλλάξουμε το επίπεδο πρόσβασης κάνοντας κλικ στο κουμπί ``Settings'' στην <<r_team_page>>.

[[r_team_page]]
.Η σελίδα της ομάδας.
image::images/orgs-02-teams.png[]

Όταν προσκαλούμε κάποιον σε μια ομάδα, θα λάβει ένα e-mail που θα τους ενημερώνει ότι έχουν προσκληθεί.

Επιπλέον, τα `@mentions` της ομάδας (όπως π.χ. `@acmecorp/frontend`) λειτουργούν λίγο-πολύ όπως και για μεμονωμένους μεμονωμένους χρήστες με τη διαφορά ότι *όλα* τα μέλη της ομάδας αποκτούν συνδρομή στο νήμα.
Αυτό είναι χρήσιμο εάν θέλουμε να τραβήξουμε την προσοχή κάποιου σε μια ομάδα, αλλά δεν ξέρουμε ακριβώς ποιον να ρωτήσουμε.

Ένας χρήστης μπορεί να ανήκει πολλές ομάδες, οπότε μην περιορίζεστε μόνο σε ομάδες ελέγχου πρόσβασης.
Οι ομάδες ειδικού ενδιαφέροντος, όπως π.χ. `ux`, `css` ή `refactoring`, είναι χρήσιμες για ορισμένα είδη ερωτήσεων και άλλες όπως οι `legal` και `colorblind` για εντελώς άλλα.

==== Μητρώο ελέγχων

Επιπλέον, οι οργανισμοί παρέχουν στους ιδιοκτήτες πρόσβαση σε όλες τις πληροφορίες σχετικά με το τι συνέβαινε στο πλαίσιο του οργανισμού.
Μπορούμε να μεταβούμε στην καρτέλα ``Audit Log'' (``Μητρώο ελέγχων'') και να δούμε τι συνέβη σε επίπεδο οργανισμού, ποιος το έκανε και πού έγινε.

[[r_audit_log]]
.Το μητρώο ελέγχων.
image::images/orgs-03-audit.png[Το μητρώο ελέγχων.]

Μπορούμε επίσης να φιλτράρουμε συγκεκριμένους τύπους συμβάντων, συγκεκριμένους τόπους ή συγκεκριμένα άτομα.



=== Συγγραφή script στο GitHub

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

Ευτυχώς για μας, το GitHub είναι πραγματικά αρκετά παραβιάσιμο (hackable) με πολλούς τρόπους.
Σε αυτήν την ενότητα θα περιγράψουμε πώς να χρησιμοποιήσουμε το σύστημα αγκίστρων του GitHub και το API του, ώστε να επιβάλλουμε το GitHub να λειτουργεί όπως θέλουμε.

==== Άγκιστρα

Η ενότητα Αγκίστρων και Υπηρεσιών της διαχείρισης ενός αποθετηρίου GitHub είναι ο ευκολότερος τρόπος να κάνουμε το GitHub να επικοινωνήσει με εξωτερικά συστήματα.

===== Υπηρεσίες

Πρώτα θα ρίξουμε μια ματιά στις Υπηρεσίες.
Οι ενσωματώσεις τόσο αγκίστρων όσο και υπηρεσιών βρίσκονται στην ενότητα ``Settings'' του αποθετηρίου, όπου προηγουμένως εξετάσαμε την προσθήκη συνεργατών και την αλλαγή του προεπιλεγμένου κλάδου του έργου μας.
Στην καρτέλα ``Webhooks and Services'' θα δούμε κάτι σαν την εικόνα <<r_services_hooks>>

[[r_services_hooks]]
.Ενότητα ρυθμίσεων υπηρεσιών και αγκίστρων.
image::images/scripting-01-services.png[Υπηρεσίες και άγκιστρα.]

Υπάρχουν δεκάδες υπηρεσίες από τις οποίες μπορούμε να επιλέξουμε, κυρίως υπηρεσίες ενσωμάτωσης σε άλλα εμπορικά ή ανοιχτά συστήματα.
Οι περισσότερες από αυτές προορίζονται για υπηρεσίες _συνεχούς ενσωμάτωσης_ (continuous integration), παρακολούθησης σφαλμάτων και προβλημάτων, συστήματα χώρων συζήτησης (chat room) και συστήματα τεκμηρίωσης.
Θα δούμε βήμα-βήμα τη δημιουργία ενός πολύ απλού αγκίστρου, του αγκίστρου ηλεκτρονικού ταχυδρομείου.
Εάν επιλέξουμε ``Email'' από την αναπτυσσόμενη λίστα ``Add Service'', θα πάρουμε μια οθόνη διαμόρφωσης όπως στην εικόνα <<r_service_config>>

[[r_service_config]]
.Ρύθμιση της υπηρεσίας e-mail.
image::images/scripting-02-email-service.png[Ρύθμιση της υπηρεσίας e-mail]

Σε αυτήν την περίπτωση, εάν πατήσουμε το κουμπί ``Add service'', η διεύθυνση e-mail που καθορίσαμε θα λάβει ένα μήνυμα e-mail κάθε φορά που κάποιος ωθεί στο αποθετήριο.
Οι υπηρεσίες μπορούν να ακούν πολλούς διαφορετικούς τύπους συμβάντων, αλλά οι περισσότερες ακούν μόνο συμβάντα ώθησης και στη συνέχεια κάνουν κάτι με αυτά τα δεδομένα.

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

===== Άγκιστρα

Εάν χρειαζόμαστε κάτι πιο συγκεκριμένο ή θέλουμε να ενσωματώσουμε μια υπηρεσία ή έναν ιστότοπο που δεν περιλαμβάνεται σε αυτήν τη λίστα, μπορούμε αντ' αυτού να χρησιμοποιήσουμε το πιο γενικό σύστημα αγκίστρων.
Τα άγκιστρα αποθετηρίων στο GitHub είναι αρκετά απλά.
Ορίζουμε μια διεύθυνση URL και το GitHub θα αποστείλει ένα ωφέλιμο φορτίο HTTP σε αυτήν τη διεύθυνση URL, όποτε συμβαίνει κάποιο συμβάν που θέλουμε.

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

Για να ενεργοποιήσουμε ένα άγκιστρο, κάνουμε κλικ στο κουμπί ``Add webhook'' στο <<r_services_hooks>>.
Αυτό θα μας φέρει σε μια σελίδα που μοιάζει με αυτήν της εικόνας <<r_web_hook>>

[[r_web_hook]]
.Ρύθμιση αγκίστρων ιστού.
image::images/scripting-03-webhook.png[Άγκιστρα ιστού.]

Η διαμόρφωση για ένα άγκιστρο ιστού είναι αρκετά απλή.
Στις περισσότερες περιπτώσεις απλά εισάγουμε μια διεύθυνση URL και ένα μυστικό κλειδί και κάνουμε κλικ στο ``Add webhook''.
Υπάρχουν μερικές επιλογές όσον αφορά στο για ποια συμβάντα θέλουμε το GitHub να μας στείλει ένα ωφέλιμο φορτίο —η προεπιλογή είναι να πάρει μόνο ένα ωφέλιμο φορτίο για το συμβάν `push`, όταν, δηλαδή, κάποιος ωθήσει νέο κώδικα σε οποιονδήποτε κλάδο του αποθετηρίου μας.

Ας δούμε ένα μικρό παράδειγμα μιας υπηρεσίας ιστού που μπορούμε να στήσουμε για να χειριστούμε ένα άγκιστρο ιστού.
Θα χρησιμοποιήσουμε το πλαίσιο ιστού της Ruby, Sinatra, αφού είναι αρκετά περιεκτικό και μπορούμε εύκολα να δούμε τι κάνουμε.

Ας υποθέσουμε ότι θέλουμε να λάβουμε ένα μήνυμα e-mail, αν ένα συγκεκριμένο άτομο ωθήσει σε έναν συγκεκριμένο κλάδο του έργου μας που τροποποιεί ένα συγκεκριμένο αρχείο.
Θα μπορούσαμε να το κάνουμε εύκολα με κώδικα όπως αυτός:

[source,ruby]

require sinatra require json require mail

post /payload do push = JSON.parse(request.body.read) # ανάλυσε το JSON

# μάζεψε τα δεδομένα που αναζητούμε
pusher = push["pusher"]["name"]
branch = push["ref"]
# πάρε μία λίστα από όλα τα αρχεία που τροποποιήθηκαν
files = push["commits"].map do |commit|
  commit['added'] + commit['modified'] + commit['removed']
end
files = files.flatten.uniq
# έλεγξε τα κριτήρια
if pusher == 'schacon' &&
   branch == 'ref/heads/special-branch' &&
   files.include?('special-file.txt')
    Mail.deliver do
      from     'tchacon@example.com'
      to       'tchacon@example.com'
      subject  'Scott Changed the File'
      body     "ALARM"
    end
  end
end
Εδώ παίρνουμε το ωφέλιμο φορτίο JSON που μας παραδίδει το GitHub και αναζητούμε σε αυτό ποιος το ώθησε, σε ποιον κλάδο ωθήθηκε και ποια αρχεία είχαν τροποποιηθεί σε όλες τις υποβολές που ωθήθηκαν.
Στη συνέχεια αντιπαραβάλλουμε αυτές τις πληροφορίες με τα κριτήρια μας και στέλνουμε ένα μήνυμα e-mail, αν ικανοποιούνται.

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

[[r_web_hook_debug]]
.Πληροφορίες εντοπισμού σφαλμάτων σε άγκιστρο ιστού.
image::images/scripting-04-webhook-debug.png[Εντοπισμός σφαλμάτων σε άγκιστρο ιστού.]

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

Για περισσότερες πληροφορίες σχετικά με τον τρόπο εγγραφής των αγκίστρων ιστού και όλων των διαφορετικών τύπων συμβάντων που μπορούμε να ακούσουμε, μπορούμε να μεταβούμε στην τεκμηρίωση του GitHub Developer στη διεύθυνση: https://developer.github.com/webhooks/[]

==== Το API του GitHub

(((GitHub, API)))
Οι υπηρεσίες και τα άγκιστρα μάς δίνουν έναν τρόπο να λαμβάνουμε ειδοποιήσεις ωθήσεων για γεγονότα που συμβαίνουν στα αποθετήριά μας.
Αλλά τι γίνεται αν χρειαζόμαστε περισσότερες πληροφορίες σχετικά με αυτά τα γεγονότα;
Τι γίνεται αν πρέπει να αυτοματοποιήσουμε κάποια ενέργεια όπως την προσθήκη συνεργατών ή επισήμανση ζητημάτων;

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

==== Στοιχειώδης χρήση

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

[source,javascript]

$ curl https://api.github.com/users/schacon { "login": "schacon", "id": 70, "avatar_url": "https://avatars.githubusercontent.com/u/70", # … "name": "Scott Chacon", "company": "GitHub", "following": 19, "created_at": "2008-01-27T17:19:28Z", "updated_at": "2014-06-10T02:37:23Z" }

Υπάρχουν πάρα πολλά τέτοια σημεία σύνδεσης για να λαμβάνουμε πληροφορίες σχετικά με οργανισμούς, έργα, θέματα, υποβολές —σχεδόν για ο,τιδήποτε μπορούμε να δούμε δημοσίως στο GitHub.
Μπορούμε ακόμη να χρησιμοποιήσουμε το API για να κάνουμε αποδώσουμε αυθαίρετη Markdown ή να βρούμε ένα πρότυπο `.gitignore`.

[source,javascript]

$ curl https://api.github.com/gitignore/templates/Java { "name": "Java", "source": "*.class

# Mobile Tools for Java (J2ME) .mtj.tmp/

# Package Files # *.jar *.war *.ear

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* " }

==== Σχολιασμός θέματος

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

Υπάρχουν πολλοί τρόποι ταυτοποίησης.
Μπορούμε να χρησιμοποιήσουμε τη βασική ταυτοποίηση μόνο με το όνομα χρήστη και τον κωδικό πρόσβασής μα, αλλά γενικά είναι καλύτερο να χρησιμοποιήσουμε ένα προσωπικό token (διακριτικό) πρόσβασης.
Μπορούμε να δημιουργήσουμε ένα τέτοιο token από την καρτέλα ``Applications'' της σελίδας ρυθμίσεων.

[[r_access_token]]
.Δημιουργία διακριτικού πρόσβασης από την καρτέλα ``Applications'' της σελίδας ρυθμίσεων.
image::images/scripting-05-access-token.png[Access Token]

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

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

Έχει επίσης το πρόσθετο πλεονέκτημα ότι αυξάνεται το επιτρεπτό όριο του ρυθμού αιτημάτων.
Χωρίς ταυτοποίηση, περιοριζόμαστε σε 60 αιτήματα ανά ώρα.
Με ταυτοποίηση μπορούμε να έχουμε μέχρι και 5.000 αιτήματα ανά ώρα.

Ας το χρησιμοποιήσουμε, λοιπόν, για να σχολιάσουμε ένα από τα ζητήματά μας.
Ας υποθέσουμε ότι θέλουμε να αφήσουμε ένα σχόλιο σε ένα συγκεκριμένο ζήτημα, το ζήτημα #6.
Για να γίνει αυτό, πρέπει να κάνουμε ένα αίτημα HTTP POST στο `repos/<χρήστης>/<αποθετήριο>/issues/<αρθ>/comments` με το διακριτικό που μόλις δημιουργήσαμε ως το πεδίο Authorization στην κεφαλίδα.

[source,javascript]

$ curl -H "Content-Type: application/json" \ -H "Authorization: token TOKEN" \ --data {"body":"A new comment, :+1:"} \ https://api.github.com/repos/schacon/blink/issues/6/comments { "id": 58322100, "html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100", …​ "user": { "login": "tonychacon", "id": 7874698, "avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2", "type": "User", }, "created_at": "2014-10-08T07:48:19Z", "updated_at": "2014-10-08T07:48:19Z", "body": "A new comment, :+1:" }

Τώρα, αν πάμε σε αυτό το ζήτημα, μπορούμε να δούμε το σχόλιο που μόλις δημοσιεύσαμε όπως στην εικόνα <<r_api_comment>>

[[r_api_comment]]
.Σχόλιο που αναρτήθηκε από το API του API.
image::images/scripting-06-comment.png[Σχόλιο που αναρτήθηκε από το API του API.]

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

==== Αλλαγή της κατάστασης ενός αιτήματος έλξης

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

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

Ας υποθέσουμε ότι έχουμε δημιουργήσει ένα άγκιστρο ιστού στο αποθετήριό μας που ενεργοποιεί μια μικρή υπηρεσία ιστού που ελέγχει αν υπάρχει η συμβολοσειρά `Signed-off-by` στο μήνυμα υποβολής.

[source,ruby]

require httparty require sinatra require json

post /payload do push = JSON.parse(request.body.read) # ανάλυσε το JSON repo_name = push[repository][full_name]

# ψάξε μέσα σε κάθε μήνυμα υποβολής
push["commits"].each do |commit|
# ψάξε για συμβολοσειρά "Signed-off-by"
if /Signed-off-by/.match commit['message']
  state = 'success'
  description = 'Successfully signed off!'
else
  state = 'failure'
  description = 'No signoff found.'
end
# post status to GitHub
sha = commit["id"]
status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}"
    status = {
      "state"       => state,
      "description" => description,
      "target_url"  => "http://example.com/how-to-signoff",
      "context"     => "validate/signoff"
    }
    HTTParty.post(status_url,
      :body => status.to_json,
      :headers => {
        'Content-Type'  => 'application/json',
        'User-Agent'    => 'tonychacon/signoff',
        'Authorization' => "token #{ENV['TOKEN']}" }
    )
  end
end
Ελπίζω ότι είναι αρκετά απλό, για να το καταλάβετε.
Σε αυτόν τον χειριστή αγκίστρων ιστού εξετάζουμε κάθε υποβολή που μόλις ωθήθηκε, αναζητούμε τη συμβολοσειρά `Signed-off-by` στο μήνυμα της υποβολής και τέλος κάνουμε POST μέσω HTTP στο σημείο σύνδεσης `/repos/<χρήστης>/<αποθετήριο>/statuses/<sha_υποβολής>` την κατάσταση.

Σε αυτήν την περίπτωση μπορούμε να στείλουμε μια κατάσταση ('επιτυχία', 'αποτυχία', 'σφάλμα'), μια περιγραφή του τι συνέβη, μια διεύθυνση URL στην οποία μπορεί να μεταβεί ο χρήστης για περισσότερες πληροφορίες και ένα ``πλαίσιο'' (context) σε περίπτωση που υπάρχουν πολλαπλές καταστάσεις για μία μόνο υποβολή.
Για παράδειγμα, μια υπηρεσία δοκιμών μπορεί να παρέχει μια κατάσταση και μια υπηρεσία επικύρωσης, όπως αυτή, μπορεί επίσης να παρέχει μια κατάσταση —το πεδίο ``context'' είναι εκεί όπου διαφοροποιούνται.

Εάν κάποιος ανοίξει ένα νέο αίτημα έλξης στο GitHub και αυτό το άγκιστρο έχει στηθεί, ίσως δούμε κάτι σαν την εικόνα <<r_commit_status>>

[[r_commit_status]]
.Κατάσταση υποβολής μέσα από το API.
image::images/scripting-07-status.png[Κατάσταση υποβολής.]

Τώρα μπορούμε να δούμε ένα μικρό πράσινο σημάδι ελέγχου δίπλα στην υποβολή που περιέχει μια συμβολοσειρά `Signed-off-by` στο μήνυμα και ένα κόκκινο σταυρό σε εκείνο στο οποίο ο συγγραφέας του ξέχασε να υπογράψει.
Μπορούμε επίσης να δούμε ότι το αίτημα έλξης την κατάσταση της τελευταίας υποβολής στον κλάδο και μας προειδοποιεί εάν έχει αποτύχει.
Αυτό είναι πραγματικά χρήσιμο εάν χρησιμοποιούμε αυτό το API για αποτελέσματα δοκιμών, έτσι ώστε να μην συγχωνεύσουμε κατά λάθος κάτι του οποίου η τελευταία υποβολή είχε αποτύχει.

==== Octokit

Παρόλο που σε αυτά τα παραδείγματα κάναμε σχεδόν τα πάντα μέσα από την `curl` και απλά αιτήματα HTTP, υπάρχουν αρκετές βιβλιοθήκες ανοιχτού κώδικα που κάνουν αυτό το API διαθέσιμο με πιο ιδιωματικό τρόπο.
Όταν γράφεται αυτό το κείμενο, οι υποστηριζόμενες γλώσσες περιλαμβάνουν τις Go, Objective-C, Ruby και .NET.
Για περισσότερες πληροφορίες σχετικά με αυτά, δεδομένου ότι χειρίζονται μεγάλο μέρος του HTTP για εμάς, μπορούμε να ανατρέξουμε στην http://github.com/octokit[].

Αυτά τα εργαλεία θα μας βοηθήσουν να εξατομικεύσουμε και να τροποποιήσουμε το GitHub ώστε να λειτουργεί καλύτερα για τις συγκεκριμένες ροές εργασίας μας.
Πλήρη τεκμηρίωση σχετικά με ολόκληρο το API καθώς και οδηγούς για συνήθεις εργασίες, μπρορούμε να βρούμε στην σελίδα https://developer.github.com[].



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

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



[#ch07-git-tools]
[[r_git_tools]]
== Εργαλεία του Git

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

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

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

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

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

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

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

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

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

[source,console]

$ 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` για να δούμε αυτήν την υποβολή, οι ακόλουθες εντολές είναι ισοδύναμες (με την προϋπόθεση ότι οι σύντομες εκδόσεις είναι μονοσήμαντες):

[source,console]

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

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

[source,console]

$ 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.
Η πιθανότητα κάθε μέλος μίας ομάδας προγραμματιστών να δεχτεί επίθεση από λύκους σε άσχετα μεταξύ τους περιστατικά το ίδιο βράδυ είναι μεγαλύτερη από αυτήν.
====

[[r_branch_references]]
==== Αναφορές κλάδων

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

[source,console]

$ git show ca82a6dff817ec66f44342007202690a93763949 $ git show topic1

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

[source,console]

$ git rev-parse topic1 ca82a6dff817ec66f44342007202690a93763949

[[r_git_reflog]]
==== RefLog Shortnames

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

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

[source,console]

$ 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:

[source,console]

$ git show HEAD@{5}

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

[source,console]

$ git show master@{yesterday}

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

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

[source,console]

$ 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 το αντιλαμβάνεται ότι σημαίνει το γονέα αυτής της υποβολής.
Ας υποθέσουμε ότι βλέπουμε το ιστορικό του έργου μας:

[source,console]

$ 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'':

[source,console]

$ 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''.
Αυτή η σύνταξη είναι χρήσιμη μόνο για υποβολές συγχώνευσης, οι οποίες έχουν περισσότερους από έναν γονείς.
Ο πρώτος γονέας είναι ο κλάδος στο οποίο βρισκόμασταν όταν συγχωνεύσαμε και ο δεύτερος είναι η υποβολή του κλάδου τον οποίο συγχωνεύσαμε:

[source,console]

$ 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` θα ήταν:

[source,console]

$ 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^^^`, το οποίο και πάλι είναι ο πρώτος γονέας του πρώτου γονέα του πρώτου γονέα:

[source,console]

$ 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` και ούτω καθεξής.

[[r_commit_ranges]]
==== Εύρος υποβολών

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

===== Διπλή τελεία

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

[[rdouble_dot]]
.Παράδειγμα ιστορικού με επιλογή εύρους.
image::images/double-dot.png[Παράδειγμα ιστορικού με επιλογή εύρους.]

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

[source,console]

$ git log master..experiment D C

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

[source,console]

$ git log experiment..master F E

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

[source,console]

$ 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` πριν από οποιαδήποτε αναφορά από την οποία δεν θέλουμε να δούμε προσπελάσιμες υποβολές.
Έτσι, αυτές οι τρεις εντολές είναι ισοδύναμες:

[source,console]

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

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

[source,console]

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

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

[[r_triple_dot]]
===== Τριπλή τελεία

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

[source,console]

$ git log master…​experiment F E D C

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

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

[source,console]

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

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



[[r_interactive_staging]]
=== Διαδραστική εργασία με το στάδιο καταχώρισης

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

Αν εκτελέσουμε την `git add` με την επιλογή `-i` ή `--interactive`, το Git μεταβαίνει σε λειτουργία διαδραστικού κελύφους, εμφανίζοντας κάτι σαν αυτό:

[source,console]

$ git add -i staged unstaged path 1: unchanged +0/-1 TODO 2: unchanged +1/-1 index.html 3: unchanged +5/-1 lib/simplegit.rb

  • Commands * 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now>

Μπορούμε να δούμε ότι αυτή η εντολή μας δείχνει μια πολύ διαφορετική εικόνα του σταδίου καταχώρισής μας —βασικά δίνει τις ίδιες πληροφορίες που παίρνουμε με την `git status` αλλά λίγο πιο συνοπτικά και ενημερωτικά.
Εμφανίζει τις αλλαγές που έχουμε προετοιμάσει για υποβολή στα αριστερά και τις υπόλοιπες στα δεξιά.

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

==== Προσθήκη σε και απόσυρση από το στάδιο καταχώρισης

Εάν πληκτρολογήσουμε `2` ή `u` στην ερώτηση `What now>`, το script μάς ρωτά ποια αρχεία που θέλουμε να προστεθούν στο στάδιο καταχώρισης:

[source,console]

What now> 2 staged unstaged path 1: unchanged +0/-1 TODO 2: unchanged +1/-1 index.html 3: unchanged +5/-1 lib/simplegit.rb Update>>

Για να προσθέσουμε τα TODO και index.html, μπορούμε να πληκτρολογήσουμε τους αριθμούς τους:

[source,console]

Update>> 1,2 staged unstaged path * 1: unchanged +0/-1 TODO * 2: unchanged +1/-1 index.html 3: unchanged +5/-1 lib/simplegit.rb Update>>

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

[source,console]

Update>> updated 2 paths

  • Commands * 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 1 staged unstaged path 1: +0/-1 nothing TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb

Τώρα μπορούμε να δούμε ότι τα αρχεία TODO και index.html έχουν προστεθεί στο στάδιο καταχώρισης ενώ το αρχείο simplegit.rb εξακολουθεί να είναι εκτός του σταδίου καταχώρισης.
Αν τώρα θέλουμε να αποσύρουμε το αρχείο TODO από το στάδιο καταχώρισης, χρησιμοποιήσουμε την επιλογή `3` ή `r` (συντομογραφία για revert):

[source,console]
  • Commands * 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 3 staged unstaged path 1: +0/-1 nothing TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb Revert>> 1 staged unstaged path

    • 1: +0/-1 nothing TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb Revert>> [enter] reverted one path

Αν κοιτάξουμε πάλι την κατάσταση του Git, θα δούμε ότι το αρχείο TODO έχει αποσυρθεί από το στάδιο καταχώρισης:

[source,console]
  • Commands * 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 1 staged unstaged path 1: unchanged +0/-1 TODO 2: +1/-1 nothing index.html 3: unchanged +5/-1 lib/simplegit.rb

Για να δούμε το diff των αρχείων που βρίσκονται στο στάδιο καταχώρισης, μπορούμε να χρησιμοποιήσουμε την εντολή `6` ή `d` (συντομογραφία για το diff).
Μας παρουσιάζει τη λίστα με τα αρχεία του σταδίου καταχώρισης και μπορούμε να επιλέξουμε εκείνα των οποίων το diff θέλουμε να δούμε.
Αυτό μοιάζει πολύ με την `git diff --cached` στη γραμμή εντολών:

[source,console]
  • Commands * 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 6 staged unstaged path 1: 1/-1 nothing index.html Review diff>> 1 diff --git a/index.html b/index.html index 4d07108..4335f49 100644 --- a/index.html ++ b/index.html @@ -16,7 +16,7 @@ Date Finder

    <p id="out">...</p>

-<div id="footer">contact : support@github.com</div> +<div id="footer">contact : email.support@github.com</div>

<script type="text/javascript">
Με αυτές τις στοιχειώδεις εντολές, μπορούμε να χρησιμοποιήσουμε τη διαδραστική λειτουργία  για να αλληλεπιδράσουμε με το στάδιο καταχώρισης λίγο πιο εύκολα.

==== Προσθήκη επιθεμάτων στο στάδιο καταχώρισης

Είναι επίσης δυνατό να προσθέσουμε στο στάδιο καταχώρισης κάποια τμήματα αρχείων και όχι τα υπόλοιπα.
Για παράδειγμα, αν κάνουμε δύο αλλαγές στο αρχείο simplegit.rb και θέλουμε να προσθέσουμε τη μία από αυτές αλλά όχι την άλλη, μπορούμε να το κάνουμε πολύ εύκολα.
Στο διαδραστικό περιβάλλον πληκτρολογούμεε `5` ή `p` (συντομογραφία για patch).
Το Git θα μας ρωτήσει ποια αρχεία θα θέλαμε να προσθέσουμε μερικώς στο στάδιο καταχώρισης· τότε για κάθε τμήμα των επιλεγμένων αρχείων, θα εμφανιστούν τα κομμάτια του diff του αρχείου και θα μας ρωτά αν θα θέλουμε να τα προσθέσουμε στο στάδιο καταχώρισης, ένα ένα:

[source,console]

diff --git a/lib/simplegit.rb b/lib/simplegit.rb index dd5ecc4..57399e0 100644 --- a/lib/simplegit.rb + b/lib/simplegit.rb @@ -22,7 +22,7 @@ class SimpleGit end

   def log(treeish = 'master')
-    command("git log -n 25 #{treeish}")
+    command("git log -n 30 #{treeish}")
   end
   def blame(path)
Stage this hunk [y,n,a,d,/,j,J,g,e,?]?
Έχουμε πολλές επιλογές σε αυτό το σημείο.
Αν πληκτρολογήσουμε `?` εμφανίζεται μια λίστα με ό,τι δυνατότητα έχουμε:

[source,console]

Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? y - stage this hunk n - do not stage this hunk a - stage this and all the remaining hunks in the file d - do not stage this hunk nor any of the remaining hunks in the file g - select a hunk to go to / - search for a hunk matching the given regex j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks e - manually edit the current hunk ? - print help

Γενικά, θα πρέπει να πληκτρολογούμε `y` ή `n` για κάθε τμήμα ανάλογα με το αν θέλουμε να το προσθέσουμε στο στάδιο καταχώρισης ή όχι αλλά η προσθήκη ή απόρριψη όλων των τμημάτων για κάποιο αρχείο είναι επίσης να είναι χρήσιμη.
Εάν θέλουμε να τοποθετήσουμε ένα τμήμα του αρχείου στο στάδιο καταχώρισης και αφήσουμε ένα άλλο τμήμα εκτός, η κατάστασή μας θα εμφανιστεί ως εξής:

[source,console]

What now> 1 staged unstaged path 1: unchanged +0/-1 TODO 2: +1/-1 nothing index.html 3: +1/-1 +4/-0 lib/simplegit.rb

Η κατάσταση του αρχείου simplegit.rb είναι ενδιαφέρουσα.
Σας δείχνει ότι μερικές γραμμές έχουν προστεθεί στο στάδιο καταχώρισης και κάποιες άλλες όχι.
Έχουμε προσθέσει αυτό το αρχείο μερικώς στο στάδιο καταχώρισης.
Σε αυτό το σημείο, μπορούμε να βγούμε από το διαδραστικό script και να εκτελέσουμε την `git commit` για να υποβάλουμε τα μερικά σταδιακά αρχεία.

Επίσης, δεν χρειάζεται να είμαστε στο διαδραστικό περιβάλλον  για να πραγματοποιήσουμε τη μερική προσθήκη στο στάδιο καταχώρισης —μπορούμε να ξεκινήσουμε το ίδιο script χρησιμοποιώντας την `git add -p` ή `git add --patch` στη γραμμή εντολών.

Επιπλέον, μπορούμε να χρησιμοποιήσουμε τη λειτουργία `patch` για μερική επαναφορά των αρχείων με την εντολή `reset --patch`, για ενημέρωση (checkout) τμημάτων των αρχείων με την εντολή `checkout --patch` και για εναπόθεση (stashing) τμημάτων των αρχείων με την εντολή `stash save --patch`.
Θα δούμε περισσότερες λεπτομέρειες για καθεμία από αυτές, όταν φτάσουμε σε πιο προηγμένες χρήσεις αυτών των εντολών.



[[r_git_stashing]]
=== `stash` και `clean`

Συχνά όταν εργαζόμαστε σε ένα τμήμα του έργου μας και επικρατεί ένα μπάχαλο σε αυτό το τμήμα, θέλουμε να αλλάξουμε κλάδο για λίγο για να εργαστούμε σε κάτι άλλο.
Το πρόβλημα είναι ότι δεν θέλουμε να κάνουμε υποβολή μισοτελειωμένης δουλειάς, αλλά να μπορέσουμε να επιστρέψουμε σε αυτό το σημείο αργότερα.
Η απάντηση σε αυτό το πρόβλημα είναι η εντολή `git stash`.

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

==== Φύλαξη της εργασίας μας σε απόθεμα

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

[source,console]

$ git status Changes to be committed: (use "git reset HEAD <file>…​" to unstage)

modified:   index.html

Changes not staged for commit: (use "git add <file>…​" to update what will be committed) (use "git checkout — <file>…​" to discard changes in working directory)

modified:   lib/simplegit.rb
Τώρα θέλουμε να αλλάξουμε κλάδο, αλλά δεν θέλουμε να υποβάλουμε ακόμα αυτά στα οποία εργαζόμασταν· έτσι θα φυλάξουμε τις αλλαγές σε ένα απόθεμα.
Για να φτιάξουμε ένα απόθεμα στη στοίβα μας, εκτελούμε `git stash` ή `git stash save`:

[source,console]

$ git stash Saved working directory and index state \ "WIP on master: 049d078 added the index file" HEAD is now at 049d078 added the index file (To restore them type "git stash apply")

Ο κατάλογος εργασίας μας είναι καθαρός:

[source,console]

$ git status # On branch master nothing to commit, working directory clean

Σε αυτό το σημείο, μπορούμε εύκολα να αλλάξουμε κλάδο και να εργαστούμε αλλού· οι αλλαγές μας έχουν αποθηκευτεί στη στοίβα μας.
Για να δούμε τι αποθέματα έχουμε, μπορούμε να χρησιμοποιήσουμε την εντολή `git stash list`:

[source,console]

$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log

Σε αυτήν την περίπτωση, είχαν δημιουργηθεί δύο αποθέματα παλιότερα, οπότε έχουμε πρόσβαση σε τρία διαφορετικά αποθέματα.
Μπορούμε να ξαναεφαρμόσουμε κάτι που είχαμε κρατήσει σε απόθεμα χρησιμοποιώντας την εντολή που εμφανίζεται στην έξοδο της βοήθειας της αρχικής εντολής `stash`: `git stash apply`.
Εάν θέλουμε να εφαρμόσουμε ένα από τα παλιότερα αποθέματα, μπορούμε να το καθορίσουμε με το όνομά του, ως εξής: `git stash apply stash@{2}`.
Εάν δεν καθορίσουμε κάποιο απόθεμα, το Git αντιλαμβάνεται ότι θέλουμε το πιο πρόσφατο και προσπαθεί να το εφαρμόσει:

[source,console]

$ git stash apply # On branch master # Changed but not updated: # (use "git add <file>…​" to update what will be committed) # # modified: index.html # modified: lib/simplegit.rb #

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

Οι αλλαγές των αρχείων μας ξαναεφαρμόστηκαν, αλλά το αρχείο που είχαμε τοποθετήσει στο στάδιο καταχώρισης εμφανίστηκε εκεί.
Για να γίνει αυτό, πρέπει να εκτελέσουμε την εντολή `git stash apply` με επιλογή `--index' ώστε να πούμε στην εντολή να προσπαθήσει να ξαναεφαρμόσει και τις αλλαγές στο στάδιο καταχώρισης.
Εάν είχαμε τρέξει αυτό, θα είχαμε επιστρέψει στην αρχική μας κατάσταση:

[source,console]

$ git stash apply --index # On branch master # Changes to be committed: # (use "git reset HEAD <file>…​" to unstage) # # modified: index.html # # Changed but not updated: # (use "git add <file>…​" to update what will be committed) # # modified: lib/simplegit.rb #

Η επιλογή `apply` απλά προσπαθεί να εφαρμόσει το απόθεμα —συνεχίζουμε να το έχουμε στη στοίβα μας.
Για να το μεταφέρουμε εκτός στοίβας, εκτελούμε `git stash drop` με το όνομα του αποθέματος που θέλουμε να ξεφορτωθούμε:

[source,console]

$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log $ git stash drop stash@{0} Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Μπορούμε επίσης να εκτελέσουμε την `git stash pop` για να εφαρμόσουμε το απόθεμα και συγχρόνως να το μεταφέρουμε εκτός στοίβας.

[NOTE]
====
.Μετακίνηση προς την `git stash push`

Από τον Οκτώβριο του 2017, υπάρχει εκτενής συζήτηση στην ηλεκτρονική λίστα του Git σχετικά με το κατά πόσο η εντολή `git stash save` έχει απαξιωθεί και αντικατασταθεί από την εναλλακτική `git stash push`. Ο κύριος λόγος είναι ότι η `git stash push` εισάγει την επιλογή να φυλάξουμε στο απόθεμά μας επιλεγμένα _pathspecs_, κάτι που δεν μπορεί να κάνει η `git stash save`.

Η εντολή `git stash save` δεν πρόκειται να μας εγκαταλείψει σύντομα, συνεπώς δεν υπάρχει λόγος ανησυχίας ότι θα εξαφανιστεί ξαφνικά. Αλλά ίσως είναι καλή τακτική να αρχίσουμε σιγά-σιγά να μετακινούμαστε προς την εναλλακτική με το `push` εξαιτίας της νέας λειτουργικότητας.
====

==== Διαφορετικές χρήσεις της `stash`

Υπάρχουν μερικές χρήσεις της `stash` που μπορούν επίσης να είναι χρήσιμες.
Η πρώτη επιλογή που είναι αρκετά δημοφιλής είναι η επιλογή `--keep-index` στην εντολή `stash save`.
Αυτή λέει στο Git να μην φυλάξει στο απόθεμα τίποτα από ό,τι έχουμε προσθέσει προηγουμένως στο στάδιο καταχώρισης με την εντολή `git add`.

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

[source,console]

$ git status -s M index.html M lib/simplegit.rb

$ git stash --keep-index Saved working directory and index state WIP on master: 1b65b17 added the index file HEAD is now at 1b65b17 added the index file

$ git status -s M index.html

Μία άλλη συνηθισμένη ενέργεια που μπορεί να θέλουμε να κάνουμε με την `stash` είναι να φυλάξουμε σε ένα απόθεμα τα μη-παρακολουθούμενα αρχεία όπως και τα παρακολουθούμενα.
Εκ προεπιλογής, η `git stash` αποθηκεύει μόνο αρχεία που βρίσκονται ήδη στο ευρετήριο.
Εάν ζητήσουμε `--include-untracked` ή σκέτο `-u`, το Git θα αποθηκεύσει επίσης όλα τα μη-παρακολουθούμενα αρχεία που έχουμε δημιουργήσει.

[source,console]

$ git status -s M index.html M lib/simplegit.rb ?? new-file.txt

$ git stash -u Saved working directory and index state WIP on master: 1b65b17 added the index file HEAD is now at 1b65b17 added the index file

$ git status -s $

Τέλος, αν ορίσουμε την επιλογή `--patch`, το Git δεν θα φυλάξει στο απόθεμα ό,τι έχει τροποποιηθεί, αλλά θα μας ρωτήσει ποιες από τις αλλαγές θέλουμε να βάλουμε στο απόθεμα και ποιες θέλουμε να κρατήσουμε στον κατάλογο εργασίας μας.

[source,console]

$ git stash --patch diff --git a/lib/simplegit.rb b/lib/simplegit.rb index 66d332e..8bb5674 100644 --- a/lib/simplegit.rb + b/lib/simplegit.rb @@ -16,6 +16,10 @@ class SimpleGit return #{git_cmd} 2>&1.chomp end end

+ + def show(treeish = master) + command("git show #{treeish}") + end

 end
 test
Stash this hunk [y,n,q,a,d,/,e,?]? y

Saved working directory and index state WIP on master: 1b65b17 added the index file

==== Δημιουργία κλάδου από απόθεμα

Εάν δημιουργήσαμε ένα απόθεμα, το αφήσουμε για λίγο και συνεχίσουμε στον κλάδο από τον οποίο το δημιουργήσαμε, μπορεί να έχουμε πρόβλημα να το ξαναεφαρμόσουμε.
Αν η `stash apply προσπαθήσει να τροποποιήσει ένα αρχείο που έχουμε ξανατροποποιήσει από τότε, θα έχουμε σύγκρουση συγχώνευσης και θα πρέπει να προσπαθήσουμε να την επιλύσουμε.
Ένας ευκολότερος τρόπο να εφαρμόσουμε τις φυλαγμένες αλλαγές ξανά, είναι να εκτελέσουμε την `git stash branch`, η οποία δημιουργεί έναν νέο κλάδο, ενημερώνει (checkout) την υποβολή στην οποία βρισκόμασταν όταν δημιουργήσαμε το απόθεμά μας, εφαρμόζει το απόθεμα σε αυτόν τον κλάδο και στη συνέχεια διαγράφει το απόθεμα, εφόσον αυτό έχει εφαρμοστεί με επιτυχία:

[source,console]

$ git stash branch testchanges Switched to a new branch "testchanges" # On branch testchanges # Changes to be committed: # (use "git reset HEAD <file>…​" to unstage) # # modified: index.html # # Changed but not updated: # (use "git add <file>…​" to update what will be committed) # # modified: lib/simplegit.rb # Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

Πρόκειται για μια βολική συντόμευση για να ανακτήσουμε εύκολα τη φυλαγμένη εργασία και να εργαστούμε σε αυτήν σε έναν νέο κλάδο.

[[r_git_clean]]
==== Συμμάζεμα του καταλόγου εργασίας

Τέλος, μπορεί να μην θέλουμε να φυλάξουμε σε απόθεμα κάποια εργασία ή αρχεία που βρίσκονται στον κατάλογο εργασίας μας, αλλά απλά να απαλλαγούμε από αυτά.
Αυτό το κάνει η εντολή `git clean`.

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

Θα πρέπει να είμαστε πολύ προσεκτικοί με αυτήν την εντολή, αφού έχει σχεδιαστεί για να αφαιρεί αρχεία από τον κατάλογο εργασίας που δεν παρακολουθούνται.
Εάν αλλάξουμε γνώμη, συχνά δεν υπάρχει τρόπος να ανακτηθούν τα περιεχομένα των αρχείων αυτών.
Μια ασφαλέστερη επιλογή είναι να εκτελέσουμε την `git stash --all` οπότε διαγράφουμε τα πάντα, αλλά επίσης τα αποθηκεύουμε σε ένα απόθεμα.

Αν θέλουμε να ξεφορτωθούμε τα αρχεία-σκουπίδια ή να καθαρίσουμε τον κατάλογο εργασίας, αυτό μπορούμε να το κάνουμε με την `git clean`.
Για να καταργήσουμε όλα τα μη-παρακολουθούμενα αρχεία στον κατάλογο εργασίας μας, μπορούμε να εκτελέσουμε την `git clean -f -d`, η οποία αφαιρεί όλα τα αρχεία καθώς και όλους τους υποκαταλόγους που αδειάζουν ως αποτέλεσμα.
Το `-f` σημαίνει ``force'' (``επίβαλε'') ή ``κάντο πραγματικά''.

Αν θέλουμε ποτέ να δούμε τι θα έκανε, μπορούμε να εκτελέσουμε την εντολή με την επιλογή `-n`, που σημαίνει ``κάνε μια γενική δοκιμή και πες μου τι _θα άλλαζες_''.

[source,console]

$ git clean -d -n Would remove test.o Would remove tmp/

Εκ προεπιλογής, η εντολή `git clean` θα αφαιρέσει μόνο μη-παρακολουθούμενα αρχεία που δεν αγνοούνται.
Οποιοδήποτε αρχείο ταιριάζει σε κάποιο μοτίβο στο αρχείο μας `.gitignore` ή άλλα αρχεία αγνόησης δεν θα αφαιρεθούν.
Εάν θέλουμε να καταργήσουμε και αυτά τα αρχεία, όπως να καταργήσουμε όλα τα αρχεία `.o` που δημιουργούνται από μία build, ώστε να μπορούμε να κάνουμε μια εντελώς καθαρή build, μπορούμε να προσθέσουμε ένα `-x` στην εντολή `git clean`.

[source,console]

$ git status -s M lib/simplegit.rb ?? build.TMP ?? tmp/

$ git clean -n -d Would remove build.TMP Would remove tmp/

$ git clean -n -d -x Would remove build.TMP Would remove test.o Would remove tmp/

Εάν δεν ξέρουμε τι πρόκειται να κάνει η εντολή `git clean`, πρέπει πάντοτε να την εκτελούμε πάντα με ένα `-n` για να διπλοελέγξουμε πριν αλλάξουμε το `-n` σε `-f` και το κάνουμε πραγματικό.
Ο άλλος τρόπος με τον οποίο μπορούμε να είμαστε προσεκτικοί σχετικά με τη διαδικασία είναι να την εκτελέσουμε με τη σημαία `-i` ή ``interactive''.

Αυτό θα εκτελέσει την εντολή `clean` σε διαδραστική λειτουργία.

[source,console]

$ git clean -x -i Would remove the following items: build.TMP test.o * Commands * 1: clean 2: filter by pattern 3: select by numbers 4: ask each 5: quit 6: help What now>

Με αυτόν τον τρόπο μπορούμε να προχωρήσουμε βήμα-βήμα σε κάθε αρχείο ξεχωριστά ή να καθορίσουμε πρότυπα για διαγραφή διαδραστικά.


[[r_signing]]
=== Υπογραφή της δουλειάς μας

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

==== Εισαγωγή στο GPG

Καταρχάς, αν θέλουμε να υπογράψουμε ο,τιδήποτε πρέπει να ρυθμίσουμε το GPG και να εγκαταστήσουμε το προσωπικό μας κλειδί.

[source,console]

$ gpg --list-keys /Users/schacon/.gnupg/pubring.gpg

pub   2048R/0A46826A 2014-06-04
uid                  Scott Chacon (Git signing key) <schacon@gmail.com>
sub   2048R/874529A9 2014-06-04
----

Εάν δεν έχουμε εγκαταστήσει ένα κλειδί, μπορούμε να δημιουργήσουμε ένα με την εντολή `gpg --gen-key`.

[source,console]
----
gpg --gen-key
----

Μόλις έχουμε ένα ιδιωτικό κλειδί για να υπογράφουμε, μπορούμε να διαμορφώσουμε το Git να το χρησιμοποιεί για να υπογράφουμε θέτοντας τη ρύθμιση `config_server.signingkey`.

[source,console]
----
git config --global user.signingkey 0A46826A
----

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

==== Υπογραφή ετικετών

Αν έχουμε ιδιωτικό κλειδί GPG, μπορούμε να το χρησιμοποιούμε για να υπογράφουμε νέες ετικέτες.
Το μόνο που έχουμε να κάνουμε είναι να χρησιμοποιήσουμε την επιλογή `-s` αντί για της `-a`:

[source,console]
----
$ git tag -s v1.5 -m 'my signed 1.5 tag'

You need a passphrase to unlock the secret key for
user: "Ben Straub <ben@straub.cc>"
2048-bit RSA key, ID 800430EB, created 2014-05-04
----

Αν εκτελέσουμε την `git show` σε αυτήν την ετικέτα, θα δούμε την υπογραφή μας GPG που είναι προσαρτημένη σε αυτήν:

[source,console]
--------
$ git show v1.5
tag v1.5
Tagger: Ben Straub <ben@straub.cc>
Date:   Sat May 3 20:29:41 2014 -0700

my signed 1.5 tag
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qrpnG+85b
hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX/YTMbm
ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
=EFTF
-----END PGP SIGNATURE-----

commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number
--------

==== Επαλήθευση ετικετών

Για να επαληθεύσουμε μια υπογεγραμμένη ετικέτα, χρησιμοποιούμε την εντολή `git -v [tag-name]`.
Αυτή η εντολή χρησιμοποιεί το GPG για να επαληθεύσει την υπογραφή.
Χρειαζόμαστε το δημόσιο κλειδί του υπογράφοντα στην κλειδοθήκη μας για να λειτουργήσει σωστά η επαλήθευση:

[source,console]
----
$ git tag -v v1.4.2.1
object 883653babd8ee7ea23e6a5c392bb739348b1eb61
type commit
tag v1.4.2.1
tagger Junio C Hamano <junkio@cox.net> 1158138501 -0700

GIT 1.4.2.1

Minor fixes since 1.4.2, including git-mv and git-http with alternates.
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Good signature from "Junio C Hamano <junkio@cox.net>"
gpg:                 aka "[jpeg image of size 1513]"
Primary key fingerprint: 3565 2A26 2040 E066 C9A7  4A7D C0C6 D9A4 F311 9B9A
----

Αν δεν διαθέτουμε το δημόσιο κλειδί του υπογράφοντα, τότε θα λάβουμε κάτι τέτοιο:

[source,console]
----
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.4.2.1'
----

[[r_signing_commits]]
==== Υπογραφή υποβολών

Σε πιο πρόσφατες εκδόσεις του Git (v1.7.9 και παραπάνω), μπορούμε πλέον να υπογράφουμε και μεμονωμένες υποβολές.
Αν ενδιαφερόμαστε να υπογράψουμε υποβολές απευθείας αντί μόνο τις ετικέτες, το μόνο που χρειάζεται να κάνουμε είναι να προσθέσουμε ένα `-S` στην εντολή μας `git commit'.

[source,console]
----
$ git commit -a -S -m 'signed commit'

You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) <schacon@gmail.com>"
2048-bit RSA key, ID 0A46826A, created 2014-06-04

[master 5c3386c] signed commit
 4 files changed, 4 insertions(+), 24 deletions(-)
 rewrite Rakefile (100%)
 create mode 100644 lib/git.rb
----

Για να δούμε και να επαληθεύσουμε αυτές τις υπογραφές, υπάρχει επίσης η επιλογή `--show-signature` στην `git log`.

[source,console]
----
$ git log --show-signature -1
commit 5c3386cf54bba0a33a32da706aa52bc0155503c2
gpg: Signature made Wed Jun  4 19:49:17 2014 PDT using RSA key ID 0A46826A
gpg: Good signature from "Scott Chacon (Git signing key) <schacon@gmail.com>"
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Jun 4 19:49:17 2014 -0700

    signed commit
----

Επιπλέον, μπορούμε να διαμορφώσουμε την `git log` να ελέγχει τυχόν υπογραφές που βρίσκει και να τις παραθέτει στην έξοδο της με τη μορφή `%G?`.

[source,console]
----
$ git log --pretty="format:%h %G? %aN  %s"

5c3386c G Scott Chacon  signed commit
ca82a6d N Scott Chacon  changed the version number
085bb3b N Scott Chacon  removed unnecessary test code
a11bef0 N Scott Chacon  first commit
----

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

Από το Git 1.8.3 και μετά, είναι δυνατό το πούμε στις `git merge` και `git pull` να επιθεωρούν και να απορρίπτουν όταν συγχωνεύσουν μια υποβολή που δεν φέρει αξιόπιστη υπογραφή GPG με την εντολή `--verify-signatures`.

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

[source,console]
----
$ git merge --verify-signatures non-verify
fatal: Commit ab06180 does not have a GPG signature.
----

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

[source,console]
----
$ git merge --verify-signatures signed-branch
Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key) <schacon@gmail.com>
Updating 5c3386c..13ad65e
Fast-forward
 README | 2 ++
 1 file changed, 2 insertions(+)
----

Μπορούμε επίσης να χρησιμοποιήσουμε την επιλογή `-S` με την ίδια την εντολή `git merge` για να υπογράψουμε την υποβολή που προκύπτει από τη συγχώνευση.
Το παρακάτω παράδειγμα αφενός επαληθεύει ότι κάθε υποβολή στον κλάδο που πρόκειται να συγχωνευθεί υπογεγραμμένη, αφετέρου υπογράφει την προκύπτουσα υποβολή συγχώνευσης.

[source,console]
----
$ git merge --verify-signatures -S  signed-branch
Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key) <schacon@gmail.com>

You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) <schacon@gmail.com>"
2048-bit RSA key, ID 0A46826A, created 2014-06-04

Merge made by the 'recursive' strategy.
 README | 2 ++
 1 file changed, 2 insertions(+)
----

==== Όλοι πρέπει να υπογράψουν

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


[[r_searching]]
=== Αναζήτηση

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

[[r_git_grep]]
==== Η `grep` του Git

Το Git συνοδεύεται από μια εντολή που ονομάζεται `grep` και μας επιτρέπει να αναζητούμε εύκολα μέσα σε οποιοδήποτε υποβεβλημένο δένδρο ή τον κατάλογο εργασίας μια συμβολοσειρά ή κανονική έκφραση (regular expression).
Για αυτά τα παραδείγματα, θα εξετάσουμε τον ίδιο τον πηγαίο κώδικα του Git.

Εκ προεπιλογής, θα εξετάσει τα αρχεία στον κατάλογο εργασίας μας.
Μπορούμε να περάσουμε την επιλογή `-n` για να εκτυπώσουμε τους αριθμούς γραμμών όπου το Git βρήκε αντιστοίχιση.

[source,console]
----
$ git grep -n gmtime_r
compat/gmtime.c:3:#undef gmtime_r
compat/gmtime.c:8:      return git_gmtime_r(timep, &result);
compat/gmtime.c:11:struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
compat/gmtime.c:16:     ret = gmtime_r(timep, result);
compat/mingw.c:606:struct tm *gmtime_r(const time_t *timep, struct tm *result)
compat/mingw.h:162:struct tm *gmtime_r(const time_t *timep, struct tm *result);
date.c:429:             if (gmtime_r(&now, &now_tm))
date.c:492:             if (gmtime_r(&time, tm)) {
git-compat-util.h:721:struct tm *git_gmtime_r(const time_t *, struct tm *);
git-compat-util.h:723:#define gmtime_r git_gmtime_r
----

Υπάρχουν πολλές ενδιαφέρουσες επιλογές που μπορούμε να δώσουμε στην εντολή `grep`.

Για παράδειγμα, αντί για την προηγούμενη κλήση, μπορούμε να βάλουμε το Git να συνοψίζει την έξοδο, δείχνοντας ακριβώς σε ποια αρχεία βρέθηκαν αντιστοιχίσεις και πόσες αντιστοιχίσεις βρέθηκαν σε κάθε αρχείο με την επιλογή `--count`:

[source,console]
----
$ git grep --count gmtime_r
compat/gmtime.c:4
compat/mingw.c:1
compat/mingw.h:1
date.c:2
git-compat-util.h:2
----

Αν θέλουμε να δούμε σε ποια μέθοδο ή συνάρτηση πιστεύει ότι έχει βρει μία αντιστοίχιση, μπορούμε να περάσουμε την επιλογή `-p`:

[source,console]
----
$ git grep -p gmtime_r *.c
date.c=static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
date.c:         if (gmtime_r(&now, &now_tm))
date.c=static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
date.c:         if (gmtime_r(&time, tm)) {
----

Έτσι, εδώ βλέπουμε ότι η `gmtime_r` καλείται στις `match_multi_number` και `match_digit` στο αρχείο `date.c`.

Μπορούμε επίσης να αναζητήσουμε σύνθετους συνδυασμούς συμβολοσειρών με την επιλογή `--and`, που διασφαλίζει ότι υπάρχουν πολλαπλές αντιστοιχίσεις βρίσκονται στην ίδια γραμμή.
Για παράδειγμα, ας αναζητήσουμε τις γραμμές που ορίζουν μια σταθερά με τις συμβολοσειρές `LINK` ή `BUF_MAX` σε αυτές στη βάση του κώδικα του Git σε μια παλαιότερη έκδοση, την  1.8.0.

Εδώ θα χρησιμοποιήσουμε επίσης τις επιλογές `--break` και `--head` που βοηθούν στον διαχωρισμό της εξόδου σε πιο ευανάγνωστη μορφή.

[source,console]
----
$ git grep --break --heading \
    -n -e '#define' --and \( -e LINK -e BUF_MAX \) v1.8.0
v1.8.0:builtin/index-pack.c
62:#define FLAG_LINK (1u<<20)

v1.8.0:cache.h
73:#define S_IFGITLINK  0160000
74:#define S_ISGITLINK(m)       (((m) & S_IFMT) == S_IFGITLINK)

v1.8.0:environment.c
54:#define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS

v1.8.0:strbuf.c
326:#define STRBUF_MAXLINK (2*PATH_MAX)

v1.8.0:symlinks.c
53:#define FL_SYMLINK  (1 << 2)

v1.8.0:zlib.c
30:/* #define ZLIB_BUF_MAX ((uInt)-1) */
31:#define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */
----

Η εντολή git grep έχει μερικά πλεονεκτήματα έναντι των κανονικών εντολών αναζήτησης όπως `grep` και `ack`.
Το πρώτο είναι ότι είναι πολύ γρήγορο, το δεύτερο είναι ότι μπορούμε να ψάξουμε μέσα σε οποιοδήποτε δέντρο στο Git, όχι μόνο στον κατάλογο εργασίας.
Όπως είδαμε στο παραπάνω παράδειγμα, αναζητήσαμε όρους σε μια παλαιότερη έκδοση του πηγαίου κώδικα Git, όχι στην έκδοση στην οποία βρισκόμαστε αυτήν τη στιγμή.

==== Αναζήτηση στο μητρώο του Git

Ίσως ψάχνουμε όιχ για το **πού** υπάρχει ένας όρος, αλλά **πότε** υπήρχε ή πότε εισήχθη.
Η εντολή `git log` έχει πολλά ισχυρά εργαλεία για την εξεύρεση συγκεκριμένων υποβολών από το περιεχόμενο των μηνυμάτων τους ή ακόμη και από το περιεχόμενο των diff που εισάγουν.

Αν θέλουμε να μάθουμε, για παράδειγμα, πότε η σταθερά `ZLIB_BUF_MAX` εισήχθη για πρώτη φορά, μπορούμε να πούμε στο Git να μας δείξει μόνο τις υποβολές που είτε πρόσθεσαν είτε αφαίρεσαν αυτήν τη συμβολοσειρά με την επιλογή `-S`.

[source,console]
----
$ git log -SZLIB_BUF_MAX --oneline
e01503b zlib: allow feeding more than 4GB in one go
ef49a7a zlib: zlib can only process 4GB at a time
----

Αν κοιτάξουμε το diff αυτών των υποβολών μπορούμε να δούμε ότι η σταθερά εισήχθη στην `ef49a7a` και τροποποιήθηκε στην `e01503b`.

Αν χρειάζεται να είμαστε πιο συγκεκριμένοι, μπορούμε να παράσχουμε μια κανονική έκφραση για αναζήτηση με την επιλογή `-G`.

===== Γραμμική αναζήτηση στο μητρώο

Μια άλλη αρκετά προηγμένη αναζήτηση στο μητρώο που είναι απίστευτα χρήσιμη είναι η γραμμική αναζήτηση στο ιστορικό.
Αυτή είναι μια αρκετά πρόσφατη προσθήκη και δεν είναι πολύ γνωστή, αλλά μπορεί να είναι πραγματικά χρήσιμη.
Καλείται με την επιλογή `-L` στην `git log` και θα μας δείξει το ιστορικό μιας συνάρτησης ή μιας γραμμής στον κώδικά μας.

Για παράδειγμα, αν θέλαμε να δούμε κάθε αλλαγή που έγινε στη συνάρτηση `git_deflate_bound` στο αρχείο `zlib.c`, θα μπορούσαμε να εκτελέσουμε `git log -L :git_deflate_bound:zlib.c`.
Αυτήη εντολή θα προσπαθήσει να καταλάβει ποια είναι τα όρια αυτής της συνάρτησης και στη συνέχεια να ψάξει μέσα στο ιστορικό και θα μας δείξει κάθε αλλαγή που έγινε στη συνάρτηση σαν μια σειρά από επιθέματα από όταν η συνάρτηση δημιουργήθηκε για πρώτη φορά.

[source,console]
----
$ git log -L :git_deflate_bound:zlib.c
commit ef49a7a0126d64359c974b4b3b71d7ad42ee3bca
Author: Junio C Hamano <gitster@pobox.com>
Date:   Fri Jun 10 11:52:15 2011 -0700

    zlib: zlib can only process 4GB at a time

diff --git a/zlib.c b/zlib.c
--- a/zlib.c
+++ b/zlib.c
@@ -85,5 +130,5 @@
-unsigned long git_deflate_bound(z_streamp strm, unsigned long size)
+unsigned long git_deflate_bound(git_zstream *strm, unsigned long size)
 {
-       return deflateBound(strm, size);
+       return deflateBound(&strm->z, size);
 }


commit 225a6f1068f71723a910e8565db4e252b3ca21fa
Author: Junio C Hamano <gitster@pobox.com>
Date:   Fri Jun 10 11:18:17 2011 -0700

    zlib: wrap deflateBound() too

diff --git a/zlib.c b/zlib.c
--- a/zlib.c
+++ b/zlib.c
@@ -81,0 +85,5 @@
+unsigned long git_deflate_bound(z_streamp strm, unsigned long size)
+{
+       return deflateBound(strm, size);
+}
+
----

Αν το Git δεν μπορεί να καταλάβει πώς να αντιστοιχίσει κάποια συνάρτηση ή μέθοδο στη γλώσσα προγραμματισμού μας, μπορούμε επίσης να του δώσουμε μία κανονική έκφραση.
Για παράδειγμα, αυτό θα έκανε το ίδιο πράγμα: `git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c`.
Θα μπορούσαμε επίσης να του δώσουμε ένα εύρος γραμμών ή έναν μοναδικό αριθμό γραμμής και θα είχαμε το ίδιο είδος εξόδου.


[[r_rewriting_history]]
=== Η ιστορία ξαναγράφεται

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

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

[[r_git_amend]]
==== Αλλαγή της τελευταίας υποβολής

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

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

[source,console]
----
$ git commit --amend
----

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

Αν έχουμε υποβάλει και στη συνέχεια θέλουμε να αλλάξουμε το στιγμιότυπο που υποβάλαμε προσθέτοντας ή αλλάζοντας αρχεία, ενδεχομένως επειδή ξεχάσαμε να προσθέσουμε ένα νεο αρχείο στην αρχική υποβολή, η διαδικασία λειτουργεί βασικά με τον ίδιο τρόπο.
Μπορούμε να επεξεργαστούμε τις αλλαγές που θέλουμε, επεξεργαζόμενοι ένα αρχείο και τρέχοντας το `git add` σε αυτό ή `git rm` σε ένα παρακολουθούμενο αρχείο και εφόσον ακολουθήσει μία `git commit --amend` θα πάρει το τρέχον στάδιο καταχώρισης και θα το κάνει στιγμιότυπο για τη νέα υποβολή.

Πρέπει να είμαστε προσεκτικοί με αυτήν την τεχνική επειδή η `amend` τροποποιεί τον αριθμό SHA-1 της υποβολής.
Είναι σαν ένα πολύ μικρό `rebase` —δεν πρέπει να τροποποιοιούμε την τελευταία μας υποβολή εάν την έχουμε ήδη ωθήσει.

[[r_changing_multiple]]
==== Αλλαγή πολλών μηνυμάτων υποβολών

Για να τροποποιήσουμε μια υποβολή που είναι πιο πίσω στο ιστορικό μας, πρέπει να χρησιμοποιήσουμε πιο πολύπλοκα εργαλεία.
Το Git δεν διαθέτει ένα εργαλείο τροποποίησης ιστορικού, αλλά μπορούμε να χρησιμοποιήσουμε το εργαλείο `rebase` για να αλλάξουμε τη βάση μιας σειράς υποβολών στον HEAD στον οποίο είχαν αρχικά βασιστεί αντί να τις μετακινήσουμε σε κάποιον άλλο.
Με το εργαλείο διαδραστικής αλλαγής βάσης, μπορούμε στη συνέχεια να σταματήσουμε μετά από κάθε υποβολή που θέλουμε να τροποποιήσουμε και να αλλάξουμε το μήνυμα, να προσθέσουμε αρχεία ή να κάνουμε ό,τι επιθυμούμε.
Μπορούμε να εκτελέσουμε την `rebase` διαδραστικά προσθέτοντας την επιλογή `-i` στην `git rebase`.
Πρέπει να υποδείξουμε πόσο πίσω θέλουμε να ξαναγράψουμε τις υποβολές λέγοντας στην εντολή ποια υποβολή να είναι η νέα βάση.

Για παράδειγμα, εάν θέλουμε να αλλάξουμε τα τελευταία τρία μηνύματα υποβολής ή κάποιο από αυτά τα μηνύματα υποβολής, δίνουμε ως όρισμα στην `git rebase -i` τον γονέα της τελευταίας υποβολής που θέλουμε να επεξεργαστούμε, που είναι `HEAD~2^` ή `HEAD~3`.
Μπορεί να είναι πιο εύκολο να θυμηθούμε το `~3` επειδή προσπαθούμε να επεξεργαστούμε τις τελευταίες τρεις υποβολές· αλλά καλό είναι θυμόμαστε ότι στην πραγματικότητα πηγαίνουμε τέσσερις υποβολές πιο πριν, στον γονέα της τελευταίας υποβολής που θέλουμε να επεξεργαστούμε:

[source,console]
----
$ git rebase -i HEAD~3
----

Ας θυμηθούμε ξανά ότι αυτή είναι μια εντολή αλλαγής βάσης —κάθε υποβολή που περιλαμβάνεται στο εύρος `HEAD~3..HEAD` θα ξαναγραφεί, είτε αλλάζουμε το μήνυμα είτε όχι.
Γι' αυτό δεν πρέπει να συμπεριλάβουμε καμία υποβολή που έχουμε ήδη ωθήσει σε κάποιον κεντρικό διακομιστή —αυτό θα προκαλέσει σύγχυση στους άλλους προγραμματιστές, αφού θα τους παρέχει μία εναλλακτική έκδοση της ίδιας αλλαγής.

Η εκτέλεση αυτής της εντολής μάς δίνει μια λίστα υποβολών στον επεξεργαστή κειμένου μας που μοιάζει με κάτι σαν αυτό:

[source,console]
----
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
----

Είναι σημαντικό να σημειώσουμε ότι αυτές οι υποβολές παρατίθενται με την αντίθετη σειρά από αυτήν που συνήθως βλέπουμε όταν χρησιμοποιούμε την εντολή `log`.
Αν εκτελέσουμε την `log ', θα δούμε κάτι σαν αυτό:

[source,console]
----
$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d added cat-file
310154e updated README formatting and added blame
f7f3f6d changed my name a bit
----

Η σειρά είναι αντίστροφη.
Η διαδραστική αλλαγή βάσης μάς δίνει ένα script, το οποίο και θα τρέξει.
Θα ξεκινήσει από την υποβολή που καθορίσαμε στη γραμμή εντολών (`HEAD~3`) και θα ``ξαναπαίξει'' τις αλλαγές που εισήχθησαν σε καθεμία από αυτές τις υποβολές από πάνω προς τα κάτω.
Εμφανίζει την παλαιότερη στην κορυφή, αντί για την πιο πρόσφατη, επειδή είναι η πρώτη που θα ξαναπαίξει.

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

[source,console]
----
edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
----

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

[source,console]
----
$ git rebase -i HEAD~3
Stopped at f7f3f6d... changed my name a bit
You can amend the commit now, with

       git commit --amend

Once you’re satisfied with your changes, run

       git rebase --continue
----

Αυτές οι οδηγίες μάς λένε ακριβώς τι να κάνουμε.
Πληκτρολογούμε:

[source,console]
----
$ git commit --amend
----

Αλλάζουμε το μήνυμα υποβολής και βγαίνουμε από τον επεξεργαστή.
Τότε τρέχουμε:

[source,console]
----
$ git rebase --continue
----

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

==== Αλλαγή βάσης υποβολών

Μπορούμε επίσης να χρησιμοποιήσουμε διαδραστικές αλλαγές βάσης για να αναδιατάξουμε ή να αφαιρέσουμε πλήρως υποβολές.
Εάν θέλουμε να αφαιρέσουμε την υποβολή `add cat-file` και να αλλάξουμε τη σειρά με την οποία εισήχθησαν οι άλλες δύο υποβολές, μπορούμε να αλλάξουμε το script αλλαγής βάσης από αυτό:

[source,console]
----
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
----

σε αυτό:

[source,console]
----
pick 310154e updated README formatting and added blame
pick f7f3f6d changed my name a bit
----

Όταν αποθηκεύουμε και εξερχόμαστε από τον επεξεργαστή, το Git επιστρέφει τον κλάδο μας στον γονέα αυτών των υποβολών, εφαρμόζει την `310154e` και στη συνέχεια `f7f3f6d` και στη συνέχεια σταματά.
Ουσιαστικά αλλάζουμε τη σειρά αυτών των υποβολών και αφαιρούμε πλήρως την υποβολή `add cat-file`.

[[r_squashing]]
==== Squashing Commits

Είναι επίσης δυνατό να πάρουμε μιας ακολουθία υποβολών και να τις πολτοποιήσουμε σε μια ενιαία υποβολή με το εργαλείο διαδραστικής αλλαγής βάσης.
Το script παρέχει χρήσιμες οδηγίες στο μήνυμα της `rebase`:

[source,console]
----
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
----

Εάν, αντί για `pick` ή `edit`, καθορίσουμε `squash`, το Git εφαρμόζει τόσο αυτήν την υποβολή όσο και την υποβολή ακριβώς πριν από αυτήν και μας κάνει να συγχωνεύσουμε τα μηνύματα των υποβολών.
Επομένως εάν θέλουμε να πραγματοποιήσουμε μόνο μία υποβολή από αυτές τις τρεις υποβολές, θα κάνουμε το script να μοιάζει με αυτό:

[source,console]
----
pick f7f3f6d changed my name a bit
squash 310154e updated README formatting and added blame
squash a5f4a0d added cat-file
----

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

[source,console]
----
# This is a combination of 3 commits.
# The first commit's message is:
changed my name a bit

# This is the 2nd commit message:

updated README formatting and added blame

# This is the 3rd commit message:

added cat-file
----

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

==== Διαχωρισμός υποβολών

Ο διαχωρισμός μιας υποβολής αναιρεί την υποβολή και στη συνέχεια προσθέτει στο στάδιο καταχώρισης και υποβάλλει τόσες υποβολές όσες θέλουμε.
Για παράδειγμα, ας υποθέσουμε ότι θέλουμε να χωρίσουμε τη μεσαία από τις τρεις υποβολές μας.
Αντί της `updated README formatting and added blame`, θέλουμε να τη χωρίσουμε σε δύο υποβολές: `updated README formatting` για την πρώτη και `added blame` για τη δεύτερη.
Μπορούμε να το κάνουμε αυτό στο script `rebase -i` αλλάζοντας την εντολή για την υποβολή που θέλουμε να χωρίσουμε σε `edit`:

[source,console]
----
pick f7f3f6d changed my name a bit
edit 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
----

Στη συνέχεια, όταν το script μάς βγάζει στη γραμμή εντολών, επαναφέρουμε (reset) αυτήν την υποβολή, παίρνουμε τις αλλαγές που έχουν αναιρεθεί και δημιουργούμε πολλαπλές υποβολές από αυτές τις αλλαγές.
Όταν αποθηκεύουμε και βγαίνουμε από τον επεξεργαστή κειμένου, το Git επιστρέφει στον γονέα της πρώτης υποβολής της λίστας, εφαρμόζει την πρώτη υποβολή (`f7f3f6d`), εφαρμόζει τη δεύτερη (`310154e`) και μας επαναφέρει στην κονσόλα.
Εκεί, μπορούμε να εκτελέσουμε μια μεικτή επαναφορά αυτής της υποβολής με την `git reset HEAD^`, το οποίο ουσιαστικά αναιρεί αυτήν την υποβολή και αφήνει τα τροποποιημένα αρχεία εκτός σταδίου καταχώρισης.
Τώρα μπορούμε να προσθέσουμε αρχεία στο στάδιο καταχώρισης και να τα υποβάλλουμε μέχρι να έχουμε αρκετές υποβολές και τρέχουμε την `git rebase --continue` όταν τελειώσουμε:

[source,console]
----
$ git reset HEAD^
$ git add README
$ git commit -m 'updated README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'added blame'
$ git rebase --continue
----

Το Git εφαρμόζει την τελευταία υποβολή (`a5f4a0d`) στο script και το ιστορικό μας μοιάζει με αυτό:

[source,console]
----
$ git log -4 --pretty=format:"%h %s"
1c002dd added cat-file
9b29157 added blame
35cfb2b updated README formatting
f3cc40e changed my name a bit
----

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

==== Η πυρηνική επιλογή: `filter-branch`

Υπάρχει μια άλλη επιλογή επανεγγραφής ιστορικού που μπορούμε να χρησιμοποιήσουμε αν χρειάζεται να ξαναγράψουμε έναν μεγαλύτερο αριθμό υποβολών χρησιμοποιώντας script —π.χ. για να αλλάξουμε τη διεύθυνση e-mail μας παντού ή να αφαιρέσουμε ένα αρχείο από κάθε υποβολή.
Η σχετική εντολή είναι η `filter-branch` και μπορεί να ξαναγράψει τεράστια τμήματα του ιστορικού μας, επομένως ενδεχομένως δεν πρέπει να τη χρησιμοποιήσουμε, εκτός εάν το έργο μας δεν είναι ακόμη δημόσια και άλλοι δεν έχουν βασίσει τη δουλειά τους στις υποβολές που πρόκειται να ξαναγράψουμε.
Ωστόσο, μπορεί να είναι πολύ χρήσιμο.
Θα δούμε μερικές κοινές χρήσεις της, ώστε να έχουμε μια ιδέα για κάποια από τα πράγματα που είναι σε θέση να κάνει.


[[r_removing_file_every_commit]]
===== Αφαίρεση ενός αρχείου από κάθε υποβολή

Αυτό συμβαίνει αρκετά συχνά.
Κάποιος υποβάλλει ένα τεράστιο δυαδικό αρχείο με ένα άσκοπο `git add` και θέλουμε να το αφαιρέσουμε από παντού.
Ίσως υποβάλαμε κατά λάθος ένα αρχείο που περιείχε έναν κωδικό πρόσβασης και θέλουμε να κάνουμε το έργο μας ανοιχτή πρόσβασης.
Η `filter-branch` είναι το εργαλείο που θέλουμε να χρησιμοποιήσουμε για να συμμαζέψουμε ολόκληρο το ιστορικό μας.
Για να καταργήσουμε ένα αρχείο που ονομάζεται passwords.txt παντού στο ιστορικό μας, μπορούμε να χρησιμοποιήσουμε την επιλογή `--tree-filter` με την `filter-branch`:

[source,console]
----
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten
----

Η επιλογή `--tree-filter` εκτελεί την εντολή που έχει οριστεί μετά από κάθε ενημέρωση (checkout) του έργου και κατόπιν ξαναϋποβάλει τα αποτελέσματα.
Σε αυτήν την περίπτωση, αφαιρούμε ένα αρχείο που ονομάζεται passwords.txt από κάθε στιγμιότυπο, είτε υπάρχει είτε όχι.
Αν θέλουμε να καταργήσουμε όλα τα αρχεία backup του επεξεργαστή κειμένου που υποβλήθηκαν κατά λάθος, μπορούμε να εκτελέσουμε κάτι σαν την `git filter-branch --tree-filter 'rm -f *~' HEAD`.

Θα δούμε το Git να ξαναγράφει δέντρα και υποβολές και στη συνέχεια να μετακινήσει τον δείκτη του κλάδου στο τέλος.
Γενικά είναι καλή ιδέα να κάνουμε κάτι τέτοιο σε κάποιον δοκιμαστικό κλάδο και στη συνέχεια να επαναφέρουμε σκληρά τον κύριο κλάδο αφού έχουμε βεβαιωθεί ότι το αποτέλεσμα είναι αυτό που πραγματικά θέλουμε.
Για να εκτελέσουμε το `filter-branch` σε όλους τους κλάδους μας, μπορούμε να περάσουμε την επιλογή `--all` στην εντολή.

===== Κάνοντας έναν υποκατάλογο τη νέα ρίζα

Ας υποθέσουμε ότι έχουμε πραγματοποιήσει εισαγωγή από ένα άλλο σύστημα ελέγχου και έχουμε υποκαταλόγους που δεν έχουν νόημα (κορμός, ετικέτες κ.ο.κ.).
Αν θέλουμε ο υποκατάλογος `trunk` να γίνει ο νέος ριζικός κατάλογος του έργου για κάθε υποβολή, η `filter-branch` μπορεί να μας βοηθήσει και σ' αυτό:

[source,console]
----
$ git filter-branch --subdirectory-filter trunk HEAD
Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' was rewritten
----

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

===== Αλλαγή διευθύνσεων e-mail παντού

Μια άλλη συνηθισμένη περίπτωση είναι ότι ξεχάσαμε να εκτελέσουμε την `git config` για να ορίσουμε το όνομα και τη διεύθυνση e-mail μας πριν αρχίσουμε να εργαζόμαστε ή ίσως θέλουμε να κάνουμε δημόσιο ένα έργο που είχαμε στον χώρο εργασίας μας και γι' αυτό θέλουμε να αλλάξουμε όλες τις διευθύνσεις e-mail από αυτήν της εργασίας μας στην προσωπική μας διεύθυνση.
Σε κάθε περίπτωση, μπορούμε να αλλάξουμε τις διευθύνσεις e-mail σε πολλαπλές υποβολές με τη μία επίσης με την `filter-branch`.
Πρέπει να είμαστε προσεκτικοί ώστε να αλλάξουμε μόνο τις διευθύνσεις e-mail που είναι δικές μας, γι' αυτό χρησιμοποιούμε την επιλογή `--commit-filter':

[source,console]
----
$ git filter-branch --commit-filter '
        if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
        then
                GIT_AUTHOR_NAME="Scott Chacon";
                GIT_AUTHOR_EMAIL="schacon@example.com";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD
----

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


[[r_git_reset]]
=== Απομυθοποίηση της `reset`

Πριν προχωρήσουμε σε πιο εξειδικευμένα εργαλεία, ας μιλήσουμε για τα `reset` και `checkout`.
Αυτές οι εντολές είναι δύο από τα πιο δυσνόητα σημεία του Git όταν τα συναντά κανείς για πρώτη φορά.
Κάνουν τόσο πολλά πράγματα, που φαίνεται να μην υπάρχει καμία ελπίδα πραγματικής κατανόησης σωστής χρήσης τους.
Γι' αυτό, θα κάνουμε μία απλή μεταφορά.

==== Τα τρία δέντρα

Ένας ευκολότερος τρόπος για να σκεφτούμε τα `reset` και `checkout` είναι μέσα από ένα νοητική πλαίσιο στο οποίο το Git είναι διαχειριστής του περιεχομένου τριών διαφορετικών δέντρων.
Με τον όρο ``δέντρο'' εδώ εννοούμε ουσιαστικά ``συλλογή αρχείων'', όχι ειδικότερα τη δομή δεδομένων.
(Υπάρχουν μερικές περιπτώσεις στις οποίες το Ευρετήριο δεν λειτουργεί ακριβώς όπως ένα δέντρο, αλλά για τον σκοπό μας είναι ευκολότερο να το σκεφτούμε με αυτόν τον τρόπο προς το παρόν.)

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

[cols="1,2",options="header"]
|================================
| Δέντρο             | Ρόλος
| HEAD               | Στιγμιότυπο τελευταίας υποβολής, επόμενος γονέας
| Ευρετήριο          | Προτεινόμενο στιγμιότυπο για την επόμενη υποβολή
| Κατάλογος Εργασίας | Αμμοδοχείο
|================================

===== Το δέντρο HEAD

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

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

[source,console]
----
$ git cat-file -p HEAD
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
author Scott Chacon  1301511835 -0700
committer Scott Chacon  1301511835 -0700

initial commit

$ git ls-tree -r HEAD
100644 blob a906cb2a4a904a152...   README
100644 blob 8f94139338f9404f2...   Rakefile
040000 tree 99f1a6d12cb4b6f19...   lib
----

Οι εντολές `cat-file` και `ls-tree` είναι εντολές ``διοχέτευσης'' που χρησιμοποιούνται για πράγματα χαμηλότερου επιπέδου και σπάνια  χρησιμοποιούνται στην καθημερινή εργασία μας, αλλά μας βοηθούν να δούμε τι συμβαίνει εδώ.

[[r_the_index]]
===== Το Ευρετήριο

Το Ευρετήριο είναι *η προτεινόμενη επόμενη υποβολή*.
Έχουμε επίσης αναφερθεί σε αυτήν την έννοια ως ``στάδιο καταχώρισης'' του Git, καθώς αυτό εξετάζει το Git όταν τρέχουμε την `git commit'.

Από τεχνικής άποψης το Ευρετήριο δεν είναι δομή δέντρου —στην πραγματικότητα έχει υλοποιηθεί ως ισοπεδωμένο δηλωτικό (manifest— αλλά για τον σκοπό μας είναι αρκετά κοντά σε ένα δένδρο.

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

[source,console]
----
$ git ls-files -s
100644 a906cb2a4a904a152e80877d4088654daad0c859 0	README
100644 8f94139338f9404f26296befa88755fc2598c289 0	Rakefile
100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0	lib/simplegit.rb
----

Και πάλι εδώ χρησιμοποιούμε την εντολή `ls-files ', η οποία είναι περισσότερο μια εντολή παρασκηνίου που μας δείχνει με τι μοιάζει το Ευρετήριο μας αυτήν τη στιγμή.

===== Ο Κατάλογος Εργασίας

Τέλος, έχουμε τον κατάλογο εργασίας μας.
Τα άλλα δύο δέντρα αποθηκεύουν το περιεχόμενό τους με έναν αποτελεσματικό αλλά καθόλου βολικό τρόπο, μέσα στον φάκελο `.git`.
Ο κατάλογος εργασίας το απλώνει σε πραγματικά αρχεία, πράγμα που καθιστά την επεξεργασία τους πολύ πιο εύκολη για μας.
Μπορούμε να σκεφτούμε τον κατάλογο εργασίας ως *αμμοδοχείο*, όπου μπορούμε να δοκιμάσουμε αλλαγές πριν να τις υποβάλλουμε στο στάδιο καταχώρισης (Ευρετήριο) και στη συνέχεια στο ιστορικό.

[source,console]
----
$ tree
.
├── README
├── Rakefile
└── lib
    └── simplegit.rb

1 directory, 3 files
----

==== Η ροή εργασίας

Ο κύριος σκοπός του Git είναι να καταγράφει στιγμιότυπα του έργου μας σε διαδοχικά καλύτερες καταστάσεις, χειριζόμενο αυτά τα τρία δέντρα.

image::images/reset-workflow.png[]

Ας οπτικοποιήσουμε αυτήν τη διαδικασία: ας πούμε ότι πηγαίνουμε σε έναν νέο κατάλογο με μόνον ένα αρχείο σε αυτόν.
Θα ονομάσουμε αυτήν την έκδοση του αρχείου *v1* και θα το απεικονίζουμε με μπλε χρώμα.
Τώρα τρέχουμε την `git init`, η οποία θα δημιουργήσει ένα αποθετήριο Git με μια αναφορά HEAD που δείχνει σε έναν κλάδο που δεν υπάρχει (ο `master` δεν υπάρχει ακόμα).

image::images/reset-ex1.png[]

Σε αυτό το σημείο, μόνο το δέντρο του Καταλόγου Εργασίας έχει κάποιο περιεχόμενο.

Τώρα θέλουμε να υποβάλουμε αυτό το αρχείο, οπότε χρησιμοποιούμε την `git add` για να πάρουμε το περιεχόμενο που βρίσκεται στον Κατάλογο Εργασίας και να το αντιγράψουμε στο Ευρετήριο.

image::images/reset-ex2.png[]

Στη συνέχεια, τρέχουμε την `git commit`, η οποία παίρνει τα περιεχόμενα του Ευρετηρίου και τα αποθηκεύει ως μόνιμο στιγμιότυπο, δημιουργεί ένα αντικείμενο υποβολής το οποίο δείχνει σε αυτό το στιγμιότυπο και ενημερώνει τον `master` να δείχνει σε αυτήν την υποβολή.

image::images/reset-ex3.png[]

Αν εκτελέσουμε την `git status`, δεν θα δούμε αλλαγές, αφού και τα τρία δέντρα είναι τα ίδια.

Τώρα θέλουμε να κάνουμε μια αλλαγή σε αυτό το αρχείο και να την υποβάλλουμε.
Θα επαναληφθεί η ίδια διαδικασία· πρώτα αλλάζουμε το αρχείο στον Κατάλογο Εργασίας μας.
Ας ονομάσουμε αυτην την έκδοση του αρχείου *v2* και θα την απεικονίζουμε με κόκκινο χρώμα.

image::images/reset-ex4.png[]

Αν εκτελέσουμε την εντολή `git status` τώρα, το αρχείο θα εμφανιστεί με κόκκινο χρώμα με την ένδειξη ``Changes not staged for commit'', επειδή αυτή η εγγραφή διαφέρει μεταξύ του Ευρετηρίου και του Καταλόγου Εργασίας.
Στη συνέχεια τρέχουμε την `git add` σε αυτό για να το προσθέσουμε στο Ευρετήριο μας.

image::images/reset-ex5.png[]

Σε αυτό το σημείο, αν εκτελέσουμε την `git status` θα δούμε το αρχείο πράσινο κάτω από την ένδειξη `Changes to be committed` επειδή το Ευρετήριο και ο HEAD διαφέρουν —δηλαδή, η προτεινόμενη επόμενη υποβολή μας τώρα είναι διαφορετική από την τελευταία μας υποβολή.
Τέλος, τρέχουμε `git commit` για να οριστικοποιήσουμε την υποβολή.

image::images/reset-ex6.png[]

Τώρα η `git status` δεν θα μας δώσει καμία έξοδο, αφού και τα τρία δέντρα είναι τα ίδια ξανά.

Όταν μεταβαίνουμε από έναν κλάδο σε άλλον ή κλωνοποιούμε, γίνεται μία παρόμοια διαδικασία.
Όταν μεταβαίνουμε σε έναν κλάδο, αλλάζει ο *HEAD* ώστε να δείχνει στο ref του νέου κλάδου, το Ευρετήριο γεμίζει με το στιγμιότυπο αυτής της υποβολής και στη συνέχεια τα περιεχόμενα του *Ευρετηρίου* αντιγράφουνται στον *Κατάλογο Εργασίας*.

==== Ο ρόλος της `reset`

Η εντολή `reset` γίνεται κατανοητή πιο εύκολα στο παρακάτω πλαίσιο.
Για τους σκοπούς αυτών των παραδειγμάτων, ας πούμε ότι τροποποιήσαμε ξανά το `file.txt` και το υποβάλαμε για τρίτη φορά.
Έτσι τώρα το ιστορικό μας μοιάζει με αυτό:

image::images/reset-start.png[]

Ας δούμε τώρα τι ακριβώς κάνει η `reset` όταν την καλούμε.
Χειραγωγεί άμεσα αυτά τα τρία δέντρα με έναν απλό και προβλέψιμο τρόπο.
Κάνει το πολύ μέχρι τρεις βασικές λειτουργίες.

===== Βήμα 1: μετακίνηση του HEAD

Το πρώτο πράγμα που κάνει η `reset` είναι να μετακινήσει το πού δείχνει ο HEAD.
Αυτό δεν είναι το ίδιο με το να αλλάζει τον ίδιο τον HEAD (που είναι αυτό που κάνει η `checkout`). Η `reset` μετακινεί τον κλάδο στον οποίο δείχνει ο HEAD.
Αυτό σημαίνει ότι αν ο HEAD έχει τεθεί στον κλάδο `master` (δηλ. βρισκόμαστε στον κλάδο `master`), η `git reset 9e5e6a4` θα ξεκινήσει κάνοντας τον `master` να δείχνει στην `9e5e6a4`.

image::images/reset-soft.png[]

Ανεξάρτητα από το με τι διακόπτες καλούμε την `reset`, αυτό είναι το πρώτο πράγμα που θα προσπαθήσει πάντα να κάνει.
Με τον διακόπτη `--soft ', απλά θα σταματήσει εκεί.

Τώρα ας σταθούμε λίγο σε αυτό το διάγραμμα και για να συνειδητοποιήσουμε τι συνέβη: ουσιαστικά ξέκανε την τελευταία εντολή `git commit`.
Όταν εκτελούμε την `git commit`, το Git δημιουργεί μια νέα υποβολή και μετακινεί τον κλάδο στον οποίο δείχνει ο HEAD σε αυτήν.
Όταν επαναφέρουμε (`reset`) στον `HEAD~` (τον γονέα του `HEAD`), μετακινούμε τον κλάδο πίσω στο σημείο στο οποίο βρισκόταν, χωρίς να αλλάξουμε τον Ευρετήριο ή τον Κατάλογο Εργασίας.
Θα μπορούσαμε τώρα να ενημερώσουμε τον Ευρετήριο και να εκτελέσουμε ξανά την `git commit` για να ολοκληρώσουμε τι θα έκανε η `git commit --amend` (βλ. ενότητα <<r_git_amend>>).

===== Βήμα 2: Ενημέρωση του Ευρετηρίου (`--mixed`)

Υπόψη ότι αν αν εκτελέσουμε την `git status` τώρα, θα δούμε με πράσινο τη διαφορά μεταξύ του Ευρετηρίου και του τι είναι ο νέος HEAD.

Το επόμενο πράγμα που θα κάνει η `reset` είναι να ενημερώσει το Ευρετήριο με τα περιεχόμενα εκείνου του στιγμιότυπου στο οποίο δείχνει τώρα ο HEAD.

image::images/reset-mixed.png[]

Εάν καθορίσουμε την επιλογή `--mixed`, η `reset` θα σταματήσει σε αυτό το σημείο.
Αυτή είναι και η προεπιλογή, οπότε αν δεν καθορίσουμε καμία επιλογή (μόνο `git reset HEAD~` σε αυτήν την περίπτωση), η εντολή θα σταματήσει εδώ.

Τώρα ας σταθούμε για λίγο σε αυτό το διάγραμμα και να συνειδητοποιήσουμε τι συνέβη: πάλι ξέκανε την τελευταία μας υποβολή, αλλά επίσης _αφαίρεσε ό,τι υπήρχε στο στάδιο καταχώρισης_.
Επιστρέψαμε στο σημείο που είμασταν πριν εκτελέσουμε τις εντολές `git add` και `git commit`.

===== Βήμα 3: Ενημέρωση του Καταλόγου Εργασίας (`--hard`)

Το τρίτο πράγμα που μπορεί να κάνει η `reset` είναι να κάνει τον Κατάλογο Εργασίας να είναι ίδιος με το Ευρετήριο.
Αν χρησιμοποιήσουμε την επιλογή `--hard`, θα συνεχίσει να κάνει ακριβώς αυτό.

image::images/reset-hard.png[]

Ας αναλογιστούμε λοιπόν τι συνέβη.
Ξεκάναμε την τελευταία υποβολή μας, τις εντολές `git add` και `git commit` **και** όλη την εργασία που κάναμε στον Κατάλογο Εργασίας μας.

Είναι σημαντικό να σημειωθεί ότι αυτή η σημαία (`--hard`) είναι ο μόνος τρόπος για να γίνει η εντολή `reset` επικίνδυνη και μια από τις ελάχιστες περιπτώσεις όπου το Git πραγματικά θα καταστρέψει δεδομένα.
Οποιαδήποτε άλλη επίκληση της `reset` μπορεί εύκολα να αναιρεθεί, αλλά η επιλογή `--hard` δεν μπορεί, αφού αντικαθιστά με το ζόρι τα αρχεία στον Κατάλογο Εργασίας.
Στη συγκεκριμένη περίπτωση, εξακολουθούμε να έχουμε την έκδοση *v3* του αρχείου μας σε μια υποβολή στη βάση δεδομένων του Git και θα μπορούσαμε να την επαναφέρουμε εξετάζοντας το `reflog` μας, αλλά αν δεν το είχαμε υποβάλει, το Git θα είχε και πάλι αντικαταστήσει το αρχείο και αυτήν τη φορά δεν θα ήταν ανακτήσιμο.

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

Η εντολή `reset` αντικαθιστά αυτά τα τρία δέντρα με συγκεκριμένη σειρά και σταματά όταν της λέμε:

1. Μετακινεί τον κλάδο στον οποίο δείχνει ο HEAD _(εάν `--soft`, σταμάτα εδώ)_
2. Κάνει το Ευρετήριο να είναι ιδιο με τον HEAD _(εάν  όχι `--hard`, σταμάτα εδώ)_
3. Κάνει τον Κατάλογο Εργασίας να μοιάζει με το Ευρετήριο.

==== Επαναφορά με διαδρομή

Τα προηγούμενα καλύπτουν τη συμπεριφορά της `reset` στη βασική της μορφή, αλλά μπορούμε επίσης να της παράσχουμε μία διαδρομή στην οποία να δράσει.
Αν καθορίσουμε μια διαδρομή, η `reset` θα παραλείψει το Βήμα 1 και θα περιορίσει το υπόλοιπο των ενεργειών της σε ένα συγκεκριμένο αρχείο ή σύνολο αρχείων.
Αυτό πραγματικά έχει μία λογική —ο HEAD είναι απλώς ένας δείκτης και δεν μπορεί να δείχνει σε ένα τμήμα μίας υποβολής και σε ένα τμήμα μίας άλλης.
Αλλά το Ευρετήριο και ο Κατάλογος Εργασίας _μπορούν_ να ενημερώνονται εν μέρει, οπότε η `reset` συνεχίζει με τα Bήματα 2 και 3.

Ας υποθέσουμε, λοιπόν, ότι τρέχουμε την `git reset file.txt`.
Αυτή η φόρμα (αφού δεν έχουμε καθορίσει τον αριθμό SHA-1 μιας υποβολής ή έναν κλάδο και δεν έχουμε ορίσει `--soft` ή `--hard`) είναι συντομογραφία της `git reset --mixed HEAD file.txt`, η οποίο θα:

1. Μετακινήσει τον κλάδο στον οποίο δείχνει ο HEAD _(παρακάμπτεται)_
2. Κάνει το Ευρετήριο να μοιάζει με τον HEAD _(σταμάτα εδώ)_

Έτσι, ουσιαστικά απλά αντιγράφει το `file.txt` από τον HEAD στο Ευρετήριο.

image::images/reset-path1.png[]

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

image::images/reset-path2.png[]

Αυτός είναι ο λόγος για τον οποίο η έξοδος της εντολής `git status` υπονοεί ότι την εκτελούμε για να αφαιρέσουμε ένα αρχείο από το στάδιο καταχώρισης.
(Περισσότερα σχετικά με αυτό υπάρχουν στην ενότητα <<r_unstaging>>.)

Θα μπορούσαμε εξίσου εύκολα να μην αφήσουμε τον Git να υποθέσει ότι εννοούσαμε ``έλξε τα δεδομένα από τον HEAD'', καθορίζοντας μια συγκεκριμένη υποβολή από την οποία θέλαμε αν έλξουμε αυτήν την έκδοση του αρχείου.
Απλά θα έπρεπε να τρέξουμε κάτι σαν την `git reset eb43bf file.txt`.

image::images/reset-path3.png[]

Αυτό ουσιαστικά κάνει το ίδιο πράγμα με το να είχαμε επαναφέρει το περιεχόμενο του αρχείου στη *v1* στον Κατάλογο Εργασίας, να τρέξουμε `git add` σε αυτό και στη συνέχεια να το επαναφέρουμε ξανά στην *v3* (χωρίς στην πραγματικότητα να περάσουμε όλα αυτά τα στάδια).
Αν εκτελέσουμε τώρα την `git commit`, θα καταγράψει μια αλλαγή που θα επαναφέρει αυτό το αρχείο πίσω στην *v1*, παρόλο που στην πραγματικότητα ποτέ δεν το ξαναείχαμε στον Κατάλογο Εργασίας .

Είναι επίσης ενδιαφέρον να σημειώσουμε ότι όπως και η `git add`, η εντολή `reset` θα δέχεται μια επιλογή `--patch` για την αφαίρεση από το στάδιο καταχώρισης περιεχομένου κομμάτι-κομμάτι.
Επομένως, μπορούμε να αφαιρέσουμε από το στάδιο καταχώρισης ή να επαναφέρουμε περιεχόμενο, επιλεκτικά.
ert content.

==== Συναρμογή

Ας δούμε πώς να κάνουμε κάτι ενδιαφέρον με αυτήν τη νέα δύναμη —τη συναρμογή υποβολών.

Ας πούμε ότι έχουμε μια σειρά υποβολών με μηνύματα όπως `oops.`, `WIP` και `forgot this file`.
Μπορούμε να χρησιμοποιήσουμε την `reset` για να τα συναρμόσουμε γρήγορα και εύκολα σε μια ενιαία υποβολή που μας κάνει να φανούμε πραγματικά έξυπνοι.
(Στην ενότητα <<r_squashing>> υπάρχει ένας άλλος τρόπος για να το κάνουμε αυτό, αλλά σε αυτό το παράδειγμα θα χρησιμοποιήσουμε τη 'reset' επειδή είναι απλούστερο.)

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

image::images/reset-squash-r1.png[]

Μπορούμε να εκτελέσουμε την `git reset --soft HEAD~2` για να μετακινήσουμε τον κλάδο HEAD πίσω σε μια παλαιότερη υποβολή (την πρώτη υποβολή που θέλουμε να κρατήσουμε):

image::images/reset-squash-r2.png[]

Στη συνέχεια εκτελούμε ξανά την `git commit`:

image::images/reset-squash-r3.png[]

Τώρα μπορούμε να δούμε ότι το προσβάσιμο ιστορικό μας, δηλαδή το ιστορικό που θα ωθήσουμε, τώρα μοιάζει σαν να είχαμε μία υποβολή με το αρχείο `file-a.txt` (v1), και στη συνέχεια μία δεύτερη που τροποποίησε το `file-a.txt` στο v3 και πρόσθεσε το `file-b.txt`.
Η υποβολη με την έκδοση v2 του αρχείου δεν βρίσκεται πλέον στο ιστορικό.


==== Check out

Τέλος, θα μπορούσε να αναρωτηθεί κανείς ποια είναι η διαφορά ανάμεσα στις `checkout` και `reset`.
Όπως η `reset`, έτσι και η `checkout` χειρίζεται τα τρία δέντρα και είναι λίγο διαφορετική ανάλογα με το αν της δίνουμε και μια διαδρομή αρχείου ή όχι.

===== Χωρίς διαδρομή

Το τρέξιμο της `git checkout [branch]` είναι αρκετά παρόμοιο με το τρέξιμο της `git reset --hard [branch]` επειδή ενημερώνει και τα τρία δέντρα ώστε να μοιάζουν με τον `[branch]`, αλλά υπάρχουν δύο σημαντικές διαφορές.

Καταρχάς, σε αντίθεση με τη `reset --hard`, η `checkout` είναι ασφαλής για τον Κατάλογο Εργασίας· θα ελέγξει ώστε να βεβαιωθεί ότι δεν πετάει αρχεία που έχουν αλλαγές.
Στην πραγματικότητα, είναι λίγο πιο έξυπνο από αυτό —προσπαθεί να κάνει μια τετριμμένη συγχώνευση στον Κατάλογο Εργασίας, έτσι ώστε όλα τα αρχεία που _δεν_ έχουμε αλλάξει να ενημερωθούν.
Αντίθετα η `reset --hard` απλά θα αντικαταστήσει τα πάντα χωρίς κανέναν έλεγχο.

Η δεύτερη σημαντική διαφορά είναι ο τρόπος ενημέρωσης του HEAD.
Ενώ η `reset` μετακινεί τον κλάδο στον οποίο ο HEAD, η `checkout` θα μετακινήσει τον ίδιο τον HEAD για να δείξει σε κάποιον άλλο κλάδο.

Για παράδειγμα, ας πούμε ότι έχουμε τους κλάδους `master` και `develop` που δείχνουν σε διαφορετικές υποβολές και αυτήν τη στιγμή βρισκόμαστε στον `develop` (άρα ο HEAD δείχνει σε αυτόν).
Εάν εκτελέσουμε την `git reset master`, ο `develop` θα δείχνει τώρα στην ίδια υποβολή που δείχνει ο `master`.
Αν αντίθετα τρέξουμε τη `git master checkout`, δεν θα κινηθεί ο `develop` αλλά ο `HEAD`.
Ο HEAD θα δείχνει τώρα στον `master`.

Επομένως και στις δύο περιπτώσεις ο `HEAD` μετακινείται, ώστε να δείχνει στην υποβολή A, αλλά το _πώς_ γίνεται αυτό είναι πολύ διαφορετικό.
Η `reset` θα μετακινήσει τον κλάδο στον οποίο δείχνει ο `HEAD` ενώ η `checkout` μετακινεί τον ίδιο το HEAD.

image::images/reset-checkout.png[]

===== Με διαδρομή

Ο άλλος τρόπος για να εκτελέσουμε την `checkout` είναι με διαδρομή αρχείου, η οποία, όπως η `reset`, δεν μετακινεί τον `HEAD`.
Είναι ακριβώς όπως η `git reset [branch] file` ως προς το ότι ενημερώνει το Ευρετήριο με αυτό το αρχείο σε αυτήν την υποβολή, αλλά επίσης αντικαθιστά το αρχείο στον Κατάλογο Εργασίας.
Θα ήταν ακριβώς όπως η `git reset --hard [branch] file` (αν η `reset` μας επέτρεπε να τρέξουμε κάτι τέτοιο) —δεν είναι ασφαλές για τον Κατάλογο Εργασίας και δεν μετακινεί το HEAD.

Επίσης, όπως οι `git reset` και `git add`, η `checkout` δέχεται επιλογή `--patch` για να μπορούμε να επαναφέρουμε επιλεκτικά τα περιεχόμενα του αρχείου κομμάτι-κομμάτι.

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

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

Ακολουθεί ένα σκονάκι σχετικά με το ποιες εντολές επηρεάζουν ποια δέντρα.
Η στήλη `HEAD` γίνεται `REF` αν αυτή η εντολή μετακινεί την αναφορά (κλάδο) στην οποία δείχνει ο `HEAD` και `HEAD` αν μετακινεί τον ίδιο τον `HEAD`.
Ιδιαίτερη προσοχή χρειάζεται η στήλη ``Ασφαλής για ΚΕ;'' —αν αναφέρει *ΟΧΙ*, τότε πρέπει να ξανασκεφτούμε αν πραγματικά θέλουμε να εκτελέσουμε αυτήν την εντολή.

[options="header", cols="3,1,1,1,1"]
|================================
| | HEAD | Ευρετήριο | Κατάλογος Εργασίας | Ασφαλές για ΚΕ;
| *Επίπεδο υποβολής*         |      |     |     |
| `reset --soft [commit]`    | REF  | ΟΧΙ | ΟΧΙ | ΝΑΙ
| `reset [commit]`           | REF  | ΝΑΙ | ΟΧΙ | ΝΑΙ
| `reset --hard [commit]`    | REF  | ΝΑΙ | ΝΑΙ | *ΟΧΙ*
| `checkout [commit]`        | HEAD | ΝΑΙ | ΝΑΙ | ΝΑΙ
| *Επίπεδο αρχείου*          |      |     |     |
| `reset (commit) [file]`    | ΟΧΙ  | ΝΑΙ | ΟΧΙ | ΝΑΙ
| `checkout (commit) [file]` | ΟΧΙ  | ΝΑΙ | ΝΑΙ | *ΟΧΙ*
|================================


[[r_advanced_merging]]
=== Συγχωνεύσεις για προχωρημένους

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

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

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

==== Συγκρούσεις συγχωνεύσεων

Έχουμε καλύψει κάποια βασικά στοιχεία για την επίλυση των συγκρούσεων
συγχώνευσης στην ενότητα <<r_basic_merge_conflicts>>· για πιο πολύπλοκες
συγκρούσεις το Git παρέχει μερικά εργαλεία για να μας βοηθήσει να καταλάβουμε τι συμβαίνει και πώς να αντιμετωπίσουμε καλύτερα τη σύγκρουση.

Πρώτα απ' όλα, αν είναι δυνατόν, προσπαθούμε να βεβαιωθούμε ότι ο κατάλογος εργασίας μας είναι καθαρός πριν κάνουμε μια συγχώνευση που μπορεί να έχει συγκρούσεις.
Εάν έχουμε εργασία σε εξέλιξη, είτε την υποβάλουμε σε έναν προσωρινό κλάδο είτε την παρακαταθέτουμε (stash).
Αν το κάνουμε αυτό, τότε μπορούμε να ακυρώσουμε *ο,τιδήποτε* δοκιμάσουμε εδώ.
Εάν έχουμε μη αποθηκευμένες αλλαγές στον κατάλογο εργασίας μας όταν συγχωνεύουμε, ορισμένες από αυτές τις συμβουλές μπορεί να μας κάνουν να  να χάσουμε αυτήν την εργασία.

Ας δούμε ένα πολύ απλό παράδειγμα.
Έχουμε ένα πολύ απλό αρχείο Ruby που εκτυπώνει 'hello world'.

[source,ruby]
----
#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

hello()
----

Στο αποθετήριό μας, δημιουργούμε έναν νέο κλάδο που ονομάζεται `whitespace` και προχωράμε στην αλλαγή όλων των τερματισμών γραμμής Unix σε τερματισούς γραμμής DOS, δηλαδή ουσιαστικά αλλάζουμε κάθε γραμμή του αρχείου, αλλά μόνο με κενά.
Στη συνέχεια, αλλάζουμε τη γραμμή `hello world` στο `hello mundo`.

[source,console]
----
$ git checkout -b whitespace
Switched to a new branch 'whitespace'

$ unix2dos hello.rb
unix2dos: converting file hello.rb to DOS format ...
$ git commit -am 'converted hello.rb to DOS'
[whitespace 3270f76] converted hello.rb to DOS
 1 file changed, 7 insertions(+), 7 deletions(-)

$ vim hello.rb
$ git diff -b
diff --git a/hello.rb b/hello.rb
index ac51efd..e85207e 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,7 @@
 #! /usr/bin/env ruby

 def hello
-  puts 'hello world'
+  puts 'hello mundo'^M
 end

 hello()

$ git commit -am 'hello mundo change'
[whitespace 6d338d2] hello mundo change
 1 file changed, 1 insertion(+), 1 deletion(-)
----

Τώρα επιστρέφουμε στον κλάδο `master` και προσθέτουμε κάποια τεκμηρίωση στη συνάρτηση.

[source,console]
----
$ git checkout master
Switched to branch 'master'

$ vim hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb
index ac51efd..36c06c8 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# εκτυπώνει έναν χαιρετισμό
 def hello
   puts 'hello world'
 end

$ git commit -am 'document the function'
[master bec6336] document the function
 1 file changed, 1 insertion(+)
----

Προσπαθούμε να συγχωνέψουμε τον κλάδο μας `whitespace`· θα έχουμε συγκρούσεις εξαιτίας των αλλαγών στα λευκά διαστήματα.

[source,console]
----
$ git merge whitespace
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.
----

[[r_abort_merge]]
===== Απόρριψη συγχώνευσης

Έχουμε κάποιες επιλογές.
Πρώτον, ας δούμε πώς θα βγούμε από αυτήν την κατάσταση.
Εάν δεν αναμέναμε τις συγκρούσεις και δεν θέλουμε να ασχοληθούμε με αυτήν την κατάσταση, μπορούμε απλά να βγούμε από τη συγχώνευση με την εντολή `git merge --abort`.

[source,console]
----
$ git status -sb
## master
UU hello.rb

$ git merge --abort

$ git status -sb
## master
----

Η επιλογή `git merge --abort` προσπαθεί να μας επιστρέψει στην κατάστασή μας πριν εκτελέσουμε τη συγχώνευση.
Οι μόνες περιπτώσεις στις οποίες μπορεί να μην είναι σε θέση να το κάνει τέλεια, είναι εάν είχαμε μη παρακατατεθειμένες, μη υποβεβλημένες αλλαγές στον κατάλογο εργασίας όταν την τρέξαμε, αλλιώς θα έπρεπε να δουλέψει μια χαρά.

Αν για κάποιο λόγο θέλουμε απλώς να αρχίσουμε από την αρχή, μπορούμε επίσης να εκτελέσουμε την `git reset --hard HEAD` και το αποθετήριό μας  θα επιστρέψει στην τελευταία υποβεβλημένη κατάσταση.
Είναι σημαντικό να Θυμόμαστε ότι οποιαδήποτε μη υποβεβλημένη εργασία θα χαθεί, οπότε πρέπει να είμαστε σίγουροι ότι δεν θέλουμε αυτές τις αλλαγές.

===== Αγνόηση των λευκών χαρακτήρων

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

Η προεπιλεγμένη στρατηγική συγχώνευσης πάντως μπορεί να πάρει ορίσματα και μερικά από αυτά είναι για να αγνοούν σωστά τις αλλαγές στους λευκούς χαρακτήρες.
Αν δούμε ότι έχουμε πολλά προβλήματα με τους λευκούς χαρακτήρες σε μια συγχώνευση, μπορούμε απλά να την ακυρώσουμε και να την ξανακάνουμε, αυτήν τη φορά με το την επιλογή `-Xignore-all-space` ή την `-Xignore-space-change`.
Η πρώτη επιλογή αγνοεί **εντελώς** τους λευκούς χαρακτήρες κατά τη σύγκριση γραμμών, ενώ η δεύτερη αντιμετωπίζει τις αλληλουχίες ενός ή περισσότερων λευκών χαρακτήρων ως ισοδύναμες.

[source,console]
----
$ git merge -Xignore-space-change whitespace
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----

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

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

[[r_manual_remerge]]
===== Χειροκίνητη επανασυγχώνευση αρχείου

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

Αυτό που πραγματικά πρέπει να κάνουμε είναι να περάσουμε το αρχείο που προσπαθούμε να συγχωνεύσουμε μέσα από το πρόγραμμα `dos2unix` προτού δοκιμάσουμε την πραγματική συγχώνευση αρχείων.
Πώς το κάνουμε αυτό;

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

Η λήψη των τριών εκδόσεων αρχείων είναι πραγματικά εύκολη.
Το Git αποθηκεύει όλες αυτές τις εκδόσεις στο ευρετήριο κάτω από τα `stages` τα οποία έχουν το καθένα από αυτά έναν συσχετισμένο αριθμό.
Το στάδιο 1 είναι ο κοινός πρόγονος, το στάδιο 2 είναι η δική μας έκδοση  και το στάδιο 3 είναι από το `MERGE_HEAD`, δηλαδή, την έκδοση που συγχωνεύουμε (`theirs`).

Μπορούμε να εξάγουμε ένα αντίγραφο από καθεμία από αυτές τις εκδόσεις του αρχείου που βρίσκεται σε σύγκρουση με την εντολή `git show` και μια ειδική σύνταξη.

[source,console]
----
$ git show :1:hello.rb > hello.common.rb
$ git show :2:hello.rb > hello.ours.rb
$ git show :3:hello.rb > hello.theirs.rb
----

Αν θέλουμε να το γίνουμε λίγο πιο σκληροπυρηνικοί, μπορούμε επίσης να χρησιμοποιήσουμε την εντολή `ls-files -u` για να λάβουμε τα πραγματικά SHA-1 των blob του Git blobs για καθένα από αυτά τα αρχεία.

[source,console]
----
$ git ls-files -u
100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1	hello.rb
100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2	hello.rb
100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3	hello.rb
----

Το `:1:hello.rb` είναι απλά μια συντομογραφία για να αναζητήσει κανείς τον αριθμό SHA-1 εκείνου του blob.

Τώρα που έχουμε το περιεχόμενο και των τριών σταδίων στον κατάλογο εργασίας μας, μπορούμε να διορθώσουμε χειροκίνητα τη δική τους για να διορθώσουμε το πρόβλημα των λευκών διαστημάτων χώρου και να συγχωνεύσουμε ξανά το αρχείο με την ελάχιστα γνωστή εντολή `git merge-file` που κάνει ακριβώς αυτό.

[source,console]
----
$ dos2unix hello.theirs.rb
dos2unix: converting file hello.theirs.rb to Unix format ...

$ git merge-file -p \
    hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb

$ git diff -b
diff --cc hello.rb
index 36c06c8,e85207e..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,8 -1,7 +1,8 @@@
  #! /usr/bin/env ruby

 +# εκτυπώνει έναν χαιρετισμό
  def hello
-   puts 'hello world'
+   puts 'hello mundo'
  end

  hello()
----

Σε αυτό το σημείο έχουμε συγχωνεύσει το αρχείο ωραιότατα.
Στην πραγματικότητα, αυτός ο τρόπος είναι καλύτερος από την επιλογή `ignore-space-change`, διότι επιδιορθώνει πραγματικά τις αλλαγές των λευκών διαστημάτω πριν από τη συγχώνευση αντί απλά να τις αγνοεί.
Στη συγχώνευση `ignore-space-change`, στην πραγματικότητα καταλήξαμε με μερικές γραμμές με τερματισμό γραμμής DOS και με μερικές γραμμές με τερματισμό γραμμής Unix, άρα μπερδέψαμε τα πράγματα.

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

Για να συγκρίνουμε το αποτέλεσμά μας με αυτό που είχαμε στον κλάδο μας πριν από τη συγχώνευση, με άλλα λόγια, για να δούμε τι εισήγαγε η συγχώνευση, μπορούμε να εκτελέσουμε την `git diff --ours`

[source,console]
----
$ git diff --ours
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index 36c06c8..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -2,7 +2,7 @@

 # εκτυπώνει έναν χαιρετισμό
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()
----

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

Αν θέλουμε να δούμε πώς το αποτέλεσμα της συγχώνευσης διέφερε από αυτό που υπήρχε στη δική τους πλευρά, μπορούμε να εκτελέσουμε την `git diff --theirs`.
Σε αυτό και στο επόμενο παράδειγμα, πρέπει να χρησιμοποιήσουμε την επιλογή `-b` για να εξαλείψουμε τον λευκό χαρακτήρα επειδή το συγκρίνουμε με αυτό που υπάρχει στο Git, όχι το καθαρισμένο αρχείο μας `hello.theirs.rb`.

[source,console]
----
$ git diff --theirs -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index e85207e..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# εκτυπώνει έναν χαιρετισμό
 def hello
   puts 'hello mundo'
 end
----

Τέλος, βλέπουμε πώς το αρχείο έχει αλλάξει και από τις δύο πλευρές με το `git diff --base`.

[source,console]
----
$ git diff --base -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index ac51efd..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,8 @@
 #! /usr/bin/env ruby

+# εκτυπώνει έναν χαιρετισμό
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()
----

Σε αυτό το σημείο μπορούμε να χρησιμοποιήσουμε την εντολή `git clean` για να διαγράψουμε τα επιπλέον αρχεία που δημιουργήσαμε ώστε να κάνουμε τη χειροκίνητη συγχώνευση αλλά δεν χρειαζόμαστε πλέον.

[source,console]
----
$ git clean -f
Removing hello.common.rb
Removing hello.ours.rb
Removing hello.theirs.rb
----

[[r_checking_out_conflicts]]
===== Checking Out Conflicts

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

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

[source,console]
----
$ git log --graph --oneline --decorate --all
* f1270f7 (HEAD, master) update README
* 9af9d3b add a README
* 694971d update phrase to hola world
| * e3eb223 (mundo) add more tests
| * 7cff591 add testing script
| * c3ffff1 changed text to hello mundo
|/
* b7dcc89 initial hello world code
----

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

[source,console]
----
$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.
----

Θα θέλαμε να δούμε τι είναι αυτή η σύγκρουση.
Αν ανοίξουμε το αρχείο, θα δούμε κάτι τέτοιο:

[source,ruby]
----
#! /usr/bin/env ruby

def hello
<<<<<<< HEAD
  puts 'hola world'
  puts 'hello mundo'
>>>>>>> mundo
end

hello()

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

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

Ένα χρήσιμο εργαλείο είναι η εντολή `git checkout` με την επιλογή `--conflict`.
Αυτή η εντολή θα ξανά-μεταβεί στο αρχείο και θα αντικαταστήσει τις επισημάνσεις σύγκρουσης.
Αυτό είναι χρήσιμο αν θέλουμε να αλλάξουμε τη μορφή των επισημάνσεων και να ξαναπροσπαθήσουμε να επιλύσουμε τις συγκρούσεις.

Μπορούμε να περάσουμε στην επιλογή `--conflict` είτε την τιμή `diff3` είτε την `merge` (που είναι η προεπιλογή).
Εάν της περάσουμε την `diff3`, το Git θα χρησιμοποιήσει μια ελαφρώς διαφορετική έκδοση των επισημάνσεων σύγκρουσης, όχι μόνο να μας δώσει τις εκδόσεις `ours` και `theirs` αλλά και την έκδοση `base` για να πάρουμε περισσότερες πληροφορίες για το πλαίσιο της σύγκρουσης.

[source,console]

$ git checkout --conflict=diff3 hello.rb

Μόλις το τρέξουμε, το αρχείο θα μοιάζει με αυτό:

[source,ruby]

#! /usr/bin/env ruby

def hello <<<<<<< ours puts hola world ||||||| base puts hello world

  puts 'hello mundo'
>>>>>>> theirs
end

hello()

Εάν μας αρέσει αυτή η μορφή, μπορούμε να την ορίσουμε ως προεπιλογή για μελλοντικές συγκρούσεις συγχώνευσης, θέτοντας την τιμή της `merge.conflictstyle` σε `diff3`.

[source,console]

$ git config --global merge.conflictstyle diff3

Η εντολή `git checkout` μπορεί επίσης να πάρει τις επιλογές `--ours` και `--their', οι οποίες μπορεί να είναι ένας πολύ γρήγορος τρόπος για να επιλέξουμε απλά τη μια πλευρά ή την άλλη χωρίς να συγχωνεύσουμε τίποτα.

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

[[r_merge_log]]
===== Συγχώνευση μητρώου

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

Για να πάρουμε μια πλήρη λίστα με όλες τις μοναδικές υποβολές που συμπεριλήφθησαν σε οποιονδήποτε κλάδο που συμμετέχει σε αυτήν τη συγχώνευση, μπορούμε να χρησιμοποιήσουμε τη σύνταξη ``τριπλής τελείας'' που είδαμε στην ενότητα <<r_triple_dot>>.

[source,console]

$ git log --oneline --left-right HEAD…​MERGE_HEAD < f1270f7 update README < 9af9d3b add a README < 694971d update phrase to hola world > e3eb223 add more tests > 7cff591 add testing script > c3ffff1 changed text to hello mundo

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

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

[source,console]

$ git log --oneline --left-right --merge < 694971d update phrase to hola world > c3ffff1 changed text to hello mundo

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

===== Μορφή συνδυασμένου diff

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

Όταν τρέχουμε την `git diff` αμέσως μετά από μια σύγκρουση συγχώνευσης, θα μας δώσει πληροφορίες σε μια μοναδική μορφή εξόδου της diff.

[source,console]

$ git diff diff --cc hello.rb index 0399cd5,59727f0..0000000 --- a/hello.rb + b/hello.rb @@@ -1,7 -1,7 +1,11 @@@ #! /usr/bin/env ruby

  def hello
++<<<<<<< HEAD
 +  puts 'hola world'
++=======
+   puts 'hello mundo'
++>>>>>>> mundo
  end
hello()
Η μορφή αυτή ονομάζεται ``Συνδυασμένη diff'' και μας δίνει δύο στήλες δεδομένων δίπλα σε κάθε γραμμή. Η πρώτη γραμμή μας δείχνει αν αυτή η γραμμή διαφέρει (προστέθηκε ή αφαιρέθηκε) ανάμεσα στον κλάδο `ours` και το αρχείο στον κατάλογο εργασίας μας και η δεύτερη στήλη κάνει το ίδιο αλλά ανάμεσα στον κλάδο `theirs` και τον κατάλογο εργασίας μας.

Έτσι σε αυτό το παράδειγμα μπορούμε να δούμε ότι οι γραμμές `<<<<<<<` και `>>>>>>>` βρίσκονται στο αντίγραφο εργασίας αλλά δεν βρίσκονται σε καμία πλευρά της συγχώνευσης.
Αυτό έχει νόημα επειδή το εργαλείο συγχώνευσης τις βάλει εκεί δεδομένου του συγκεκριμένου πλαισίου της σύγκρουσης, αλλά αναμένει από μας τα τις αφαιρέσουμε.

Αν επιλύσουμε τη σύγκρουση και τρέξουμε ξανά την `git diff`, θα δούμε το ίδιο πράγμα αλλά είναι λίγο πιο χρήσιμο.

[source,console]

$ vim hello.rb $ git diff diff --cc hello.rb index 0399cd5,59727f0..0000000 --- a/hello.rb + b/hello.rb @@@ -1,7 -1,7 +1,7 @@@ #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end
hello()
Αυτό μας δείχνει ότι το `hola world` βρισκόταν στην πλευρά _μας_ αλλά όχι στο αντίγραφο εργασίας, ότι το `hello mundo` ήταν στο πλευρό _τους_ αλλά όχι στο αντίγραφο εργασίας και τέλος ότι το `hola mundo` δεν ήταν σε καμία πλευρά, αλλά τώρα βρίσκεται στο αντίγραφο εργασίας.
Αυτό μπορεί να είναι χρήσιμο για μία επισκόπηση πριν την υποβολή της επίλυσης.

Μπορούμε επίσης να πάρουμε αυτό από την `git log` για οποιαδήποτε συγχώνευση μετά τη συγχώνευση για να δούμε πώς κάτι επιλύθηκε.
Το Git θα εκτυπώσει σε αυτήν τη μορφή αν εκτελέσουμε την `git show` σε μια υποβολή συγχώνευσης ή εάν προσθέσουμε την επιλογή `--cc 'σε μία `git log -p` (η οποία εκ προεπιλογής εμφανίζει μόνο επιθέματα για υποβολές που δεν είναι συγχωνεύσεις).

[source,console]

$ git log --cc -p -1 commit 14f41939956d80b9e17bb8721354c33f8d5b5a79 Merge: f1270f7 e3eb223 Author: Scott Chacon <schacon@gmail.com> Date: Fri Sep 19 18:14:49 2014 +0200

Merge branch 'mundo'
Conflicts:
    hello.rb

diff --cc hello.rb index 0399cd5,59727f0..e1d0799 --- a/hello.rb + b/hello.rb @@@ -1,7 -1,7 +1,7 @@@ #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end
hello()
[[r_undoing_merges]]
==== Αναίρεση συγχώνευσης

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

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

.ακούσια υποβολή συγχώνευσης
image::images/undomerge-start.png[Ακούσια υποβολή συγχώνευσης.]

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

===== Fix the references

Εάν η ανεπιθύμητη υποβολή συγχώνευσης υπάρχει μόνο στο τοπικό αποθετήριο, η ευκολότερη και καλύτερη λύση είναι να μετακινήσουμε τους κλάδους ώστε να δείχνουν εκεί όπου θέλουμε.
Στις περισσότερες περιπτώσεις, αν μετά από μία `git merge` εκτελέσουμε την `git reset --hard HEAD~`, αυτό θα επαναφέρει τους δείκτες κλάδων, οπότε θα μοιάζουν με αυτό:

.Το ιστορικό μετά την `git reset --hard HEAD~`.
image::images/undomerge-reset.png[Το ιστορικό μετά την `git reset --hard HEAD~`.]

Καλύψαμε την `reset` στην ενότητα <<r_git_reset>>, επομένως δεν πρέπει να είναι πολύ δύσκολο να καταλάβουμε τι συμβαίνει εδώ.
Ορίστε μία γρήγορη υπενθύμιση: η `reset --hard` συνήθως περνάει από τρία βήματα:

. Μετακινούμε τον κλάδο στον οποίο δείχνει τον HEAD. Σε αυτήν την περίπτωση, θέλουμε να μετακινήσουμε τον `master` εκεί όπου βρισκόταν πριν την υποβολή συγχώνευσης (`C6`).
. Κάνουμε το ευρετήριο να μοιάζει με τον HEAD.
. Κάνουμε τον κατάλογο εργασίας να μοιάζει με το ευρετήριο.

Το μειονέκτημα αυτής της προσέγγισης είναι ότι πρόκειται για επανεγγραφή του ιστορικού, το οποίο μπορεί να είναι προβληματικό με ένα κοινό αποθετήριο.
Στην ενότητα <<r_rebase_peril>> υπάρχουν περισσότερα σχετικά με το τι μπορεί να συμβεί· με λίγα λόγια αυτό αν κάποιος άλλος έχει τις υποβολές που αλλάζουμε, θα πρέπει μάλλον να αποφύγουμε την `reset`.
Αυτή η προσέγγιση επίσης δεν θα λειτουργήσει εάν έχουν γίνει άλλες υποβολές μετά από τη συγχώνευση· η μετακίνηση των refs ουσιαστικά θα χάσει αυτές τις αλλαγές.

[[r_reverse_commit]]
===== Eπαναφορά της υποβολής

Εάν η μετακίνηση των δεικτών των κλάδων από δω κι από κει δεν μας βοηθά, το Git μάς δίνει τη δυνατότητα να κάνουμε μια νέα υποβολή, η οποία ακυρώνει όλες τις αλλαγές από μία υπάρχουσα.
Το Git ονομάζει αυτήν τη λειτουργία `revert` (``επαναφορά'') και σε αυτό το συγκεκριμένο σενάριο, θα τη χρησιμοποιήσουμε ως εξής:

[source,console]

$ git revert -m 1 HEAD [master b1d8379] Revert "Merge branch topic"

Η επισήμανση `-m 1` υποδεικνύει ποιος γονέας είναι η ``κύρια γραμμή'' και θα πρέπει να διατηρηθεί.
Όταν ξεκινάμε μια συγχώνευση στον `HEAD` (`git merge topic'), η νέα υποβολή έχει δύο γονείς: ο πρώτος είναι ο `HEAD` (`C6`) και ο δεύτερος είναι η κορυφή του κλάδου που συγχωνεύεται `C4`).
Σε αυτήν την περίπτωση, θέλουμε να αναιρέσουμε όλες τις αλλαγές που εισήχθησαν με τη συγχώνευση του γονέα #2 (`C4`), διατηρώντας όλο το περιεχόμενο από τον γονέα #1 (`C6`).

Το ιστορικό μετά την επαναφορά της υποβολής μοιάζει ως εξής:

.ιστορικό μετά την `git revert -m 1`
image::images/undomerge-revert.png[Ιστορικό μετά την  `git revert -m 1`.]

Η νέα υποβολή `^M` έχει ακριβώς το ίδιο περιεχόμενο με την `C6`, οπότε ξεκινώντας από εδώ είναι σαν να μην συνέβη ποτέ η συγχώνευση, εκτός από το γεγονός ότι οι υποβολές που τώρα δεν έχουν μεσολαβήσει εξακολουθούν να βρίσκονται στο ιστορικό του `HEAD`.
Το Git θα μπερδευτεί αν προσπαθήσουμε να συγχωνεύσουμε ξανά τον `topic` στον `master`:

[source,console]

$ git merge topic Already up-to-date.

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

.ιστορικό με κακή συγχώνευση
image::images/undomerge-revert2.png[Ιστορικό με κακή συγχώνευση.]

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

[source,console]

$ git revert ^M [master 09f0126] Revert "Revert "Merge branch topic"" $ git merge topic

.ιστορικό μετά την επανασυγχώνευση μίας συγχώνευσης που είχε επαναφερθεί
image::images/undomerge-revert3.png[στορικό μετά την επανασυγχώνευση μίας συγχώνευσης που είχε επαναφερθεί.]

Σε αυτό το παράδειγμα, οι `M` και `^M` αλληλοεξουδετερώνονται.
Η `^^M` ουσιαστικά συγχωνεύει τις αλλαγές από `C3` και `C4`, και η `C8` συγχωνεύει τις αλλαγές από την `C7`, έτσι τώρα ο `topic` συγχωνεύεται πλήρως.

==== Άλλα είδη συγχωνεύσεων

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

===== Προτίμηση ``δική μας'' ή ``δική τους''

Πρώτα απ 'όλα, υπάρχει ένα άλλο χρήσιμο πράγμα που μπορούμε να κάνουμε με τον συνήθη ``αναδρομικό'' τρόπο συγχώνευσης.
Έχουμε ήδη δει τις επιλογές `ignore-all-space` και 'ignore-space-change` που έχουν περάσει με την επιλογή `-X`, αλλά μπορούμε επίσης να πούμε στο Git να ευνοεί τη μία ή την άλλη πλευρά όταν βλέπει μια σύγκρουση.

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

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

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

[source,console]

$ git merge mundo Auto-merging hello.rb CONFLICT (content): Merge conflict in hello.rb Resolved hello.rb using previous resolution. Automatic merge failed; fix conflicts and then commit the result.

Ωστόσο, αν την τρέξουμε με `-Xours` ή `-XTheirs` δεν προκαλεί συγκρούσεις.

[source,console]

$ git merge -Xours mundo Auto-merging hello.rb Merge made by the recursive strategy. hello.rb | 2 - test.sh | 2 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 test.sh

Σε αυτήν την περίπτωση, αντί να πάρουμε επισημάνσεις σύγκρουσης στο αρχείο με `hello mundo` στη μία πλευρά και `hola world` από την άλλη, θα επιλέξει απλά `hola world`.
Ωστόσο, όλες οι άλλες αλλαγές, που δεν δημιουργούν συγκρούσεις, σε αυτόν τον κλάδο συγχωνεύονται με επιτυχία.

Αυτή η επιλογή μπορεί επίσης να μεταβιβαστεί στην εντολή `git merge-file` που είδαμε νωρίτερα τρέχοντας κάτι σαν το `git merge-file --ours` για συγχωνεύσεις μεμονωμένων αρχείων.

Εάν θέλουμε να κάνουμε κάτι τέτοιο αλλά δεν έχουμε προσπαθήσει να συγχωνεύσουμε τις αλλαγές από την άλλη πλευρά, υπάρχει μια πιο δρακόντεια επιλογή, η οποία είναι η _στρατηγική_ συγχώνευσης `ours`.
Αυτή είναι διαφορετική από την _επιλογή_ αναδρομικής συγχώνευσης  `ours`.

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

[source,console]

$ git merge -s ours mundo Merge made by the ours strategy. $ git diff HEAD HEAD~ $

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

Αυτό μπορεί συχνά να είναι χρήσιμο στο να ξεγελάσει το Git ώστε να νομίζει ότι ένας κλάδος είναι ήδη συγχωνευμένος όταν κάνει μία συγχώνευση αργότερα.
Για παράδειγμα, ας πούμε ότι διακλαδώσαμε από έναν κλάδο `release`, κάναμε κάποια εργασία σε αυτόν την οποία θα θελήσουμε να συγχωνεύσουμε ξανά τον κλάδο `master` σε κάποια στιγμή.
Εν τω μεταξύ, η διόρθωση ενός bug, `bugfix` στον `master` πρέπει να μεταφερθεί  στον κλάδο `release`.
Μπορούμε να συγχωνεύσουμε τον κλάδο `bugfix` στον κλάδο `release` και επίσης να τρέξουμε `merge -s ours` για να συγχωνεύσουμε τον ίδιο κλάδο στον `master` (παρά το ότι η διόρθωση του bug υπάρχει ήδη εκεί) έτσι ώστε όταν συγχωνεύσουμε ξανά τον κλάδο `release` να μην υπάρχουν συγκρούσεις από τη διόρθωση σφαλμάτων.

[[r_subtree_merge]]
===== Συγχώνευση υποδένδρων

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

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

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

[source,console]

$ git remote add rack_remote https://github.com/rack/rack $ git fetch rack_remote warning: no common commits remote: Counting objects: 3184, done. remote: Compressing objects: 100% (1465/1465), done. remote: Total 3184 (delta 1952), reused 2770 (delta 1675) Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. Resolving deltas: 100% (1952/1952), done. From https://github.com/rack/rack * [new branch] build → rack_remote/build * [new branch] master → rack_remote/master * [new branch] rack-0.4 → rack_remote/rack-0.4 * [new branch] rack-0.9 → rack_remote/rack-0.9 $ git checkout -b rack_branch rack_remote/master Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. Switched to a new branch "rack_branch"

Τώρα έχουμε τη ρίζα του έργου Rack στον κλάδο `rack_branch` και το δικό μας έργο στον κλάδο `master`. Εάν τα κάνουμε μεταβούμε από το ένα στο άλλο, θα δούμε ότι το κάθε έργο περιέχουν διαφορετικά αρχεία:

[source,console]

$ ls AUTHORS KNOWN-ISSUES Rakefile contrib lib COPYING README bin example test $ git checkout master Switched to branch "master" $ ls README

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

Σε αυτήν την περίπτωση, θέλουμε να έλξουμε το έργο Rack στο έργο του κλάδου `master` ως υποκατάλογο.
Αυτό μπορούμε να το κάνουμε στο Git με την εντολή `git read-tree`. Θα μάθουμε περισσότερα σχετικά με την `read-tree` και τους φίλους της στην ενότητα <<ch10-git-internals>>, αλλά προς το παρόν αρκεί να γνωρίζουμε ότι διαβάζει το ριζικό δέντρο ενός κλάδου στον τρέχον στάδιο καταχώρισης και τον κατάλογο εργασίας.
Επιστρέφουμε τώρα στον κύριο κλάδο μας και έλκουμε τον κλάδο `rack_branch` στον υποκατάλογο `rack` του κύριου κλάδου `master` του κύριου έργου μας:

[source,console]

$ git read-tree --prefix=rack/ -u rack_branch

Όταν υποβάλουμε, φαίνεται ότι έχουμε όλα τα αρχεία του Rack κάτω από αυτόν τον υποκατάλογο —σαν να τα αντιγράψαμε από ένα tarball.
Αυτό που είναι πραγματικά ενδιαφέρον είναι ότι μπορούμε να συγχωνεύσουμε αρκετά εύκολα τις αλλαγές από τον έναν κλάδο στον άλλο.
Επομένως, εάν το έργο Rack ενημερωθεί, μπορούμε να τραβήξουμε τις αλλαγές upstream μεταβαίνοντας σε εκείνο τον κλάδο και έλκοντας:

[source,console]

$ git checkout rack_branch $ git pull

Στη συνέχεια, μπορούμε να συγχωνεύσουμε αυτές τις αλλαγές πίσω στον κλάδο `master`. Για να έλξουμε τις αλλαγές και να προεπικαλύψουμε το μήνυμα commit, χρησιμοποιούμε την επιλογή `--squash`, καθώς και την επιλογή `-Xsubtree` της στρατηγικής αναδρομικής συγχώνευσης.
(Η αναδρομική στρατηγική είναι η προεπιλογή εδώ, αλλά τη συμπεριλαμβάνουμε για λόγους σαφήνειας.)

[source,console]

$ git checkout master $ git merge --squash -s recursive -Xsubtree=rack rack_branch Squash commit — not updating HEAD Automatic merge went well; stopped before committing as requested

Όλες οι αλλαγές από το έργο Rack έχουν συγχωνευτεί και είναι έτοιμες να υποβληθούν τοπικά.
Μπορούμε επίσης να κάνουμε το αντίθετο —να κάνουμε αλλαγές στον υποκατάλογο rack του κύριου κλάδου μας και στη συνέχεια να τις συγχωνεύσουμε στον κλάδο `rack_branch` αργότερα για να τις υποβάλουμε στους διαχειριστές ή να τις ωθήσουμε προς τα πάνω.

Αυτό μας δίνει έναν τρόπο να έχουμε μια ροή εργασίας κάπως παρόμοια με τη ροή εργασίας των λειτουργικών υπομονάδων χωρίς να χρησιμοποιούμε υπομονάδες (που θα καλύψουμε στην ενότητα [_git_submodules]).
Μπορούμε να διατηρούμε κλάδους με άλλα σχετικά έργα στο αποθετήριό μας και να τα συγχωνεύουμε στο έργο μας περιστασιακά.
Είναι ωραίο κατά κάποιους τρόπους· για παράδειγμα όλος ο κώδικας υποβάλλεται σε ένα μόνο μέρος.
Ωστόσο, έχει άλλα μειονεκτήματα: είναι λίγο πιο περίπλοκο και είναι πιο εύκολο να κάνουμε κάποιο λάθος στην επανένταξη των αλλαγών ή να ωθήσουμε κατά λάθος έναν κλάδο σε ένα άσχετο αποθετήριο.

Κάτι ακόμα ελαφρώς περίεργο είναι ότι για να πάρουμε μια diff ανάμεσα σε αυτό που έχουμε στον υποκατάλογό μας `rack` και τον κώδικα στον κλάδο `rack_branch` —για να δούμε αν πρέπει να τα συγχωνεύσουμε— δεν μπορούμε να χρησιμοποιήσουμε την κανονική εντολή `diff`. Αντ' αυτού, πρέπει να τρέξουμε την `git diff-tree` με τον κλάδο με τον οποίο θέλουμε να συγκρίνουμε:

[source,console]

$ git diff-tree -p rack_branch

Ή για να συγκρίνουμε τι υπάρχει στον υποκατάλογό μας `rack` σε σχέση με αυτά που υπήρχαν στον κλάδο `master` του διακομιστή την τελευταία φορά που ανακτήσαμε, μπορούμε να εκτελέσουμε

[source,console]

$ git diff-tree -p rack_remote/master

[[rch_rerere]]
=== Rerere

Η λειτουργικότητα `git rerere` είναι ένα λίγο κρυμμένο χαρακτηριστικό.
Το όνομα σημαίνει ``reuse recorded resolution'' (``επαναχρησιμοποίηση καταγεγραμμένης επίλυσης'') και όπως υποδηλώνει το όνομα, μας επιτρέπει να ζητήσουμε από το Git να θυμάται πώς έχουμε επιλύσει μια σύγκρουση τμημάτων κώδικα έτσι ώστε την επόμενη φορά που βλέπει την ίδια σύγκρουση, να την επιλύσει αυτόματα.

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

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

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

Για να ενεργοποιήσουμε τη λειτουργία `rerere`, απλά εκτελέσουμε αυτήν τη ρύθμιση παραμέτρων:

[source,console]

$ git config --global rerere.enabled true

Μπορούμε επίσης να την ενεργοποιήσουμε δημιουργώντας έναν κατάλογο `.git/rr-cache` σε ένα συγκεκριμένο αποθετήριο, αλλά η ρύθμιση της παραμέτρου με την `config` είναι σαφέστερη και μπορεί να γίνει καθολικά.

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

[source,console]

#! /usr/bin/env ruby

def hello puts hello world end

Σε έναν κλάδο αλλάζουμε τη λέξη `hello` σε `hola`, στη συνέχεια σε έναν άλλο κλάδο αλλάζουμε το `world` σε `mundo`, ακριβώς όπως πριν.

image::images/rerere1.png[]

Όταν συγχωνεύσουμε τους δύο κλάδους, θα πάρουμε σύγκρουση συγχώνευσης:

[source,console]

$ git merge i18n-world Auto-merging hello.rb CONFLICT (content): Merge conflict in hello.rb Recorded preimage for hello.rb Automatic merge failed; fix conflicts and then commit the result.

Παρατηρούμε τη νέα γραμμή `Recorded preimage for FILE`.
Αν δεν υπήρχε, θα φαινόταν ακριβώς όπως μια κανονική σύγκρουση συγχώνευσης.
Σε αυτό το σημείο, η `rerere` μπορεί να μας πει μερικά πράγματα.
Κανονικά, θα εκτελούσαμε την `git status` σε αυτό το σημείο για να δούμε τι είναι όλες οι συγκρούσεις:

[source,console]

$ git status # On branch master # Unmerged paths: # (use "git reset HEAD <file>…​" to unstage) # (use "git add <file>…​" to mark resolution) # # both modified: hello.rb #

Ωστόσο η `git rerere` θα μας πει επίσης τι έχει καταγράψει για την κατάσταση προ συγχώνευσης με την `git rerere status`:

[source,console]

$ git rerere status hello.rb

Και η `git rerere diff` θα δείξει την τρέχουσα κατάσταση της επίλυσης —με τι ξεκινήσαμε την επίλυση και τι έχουμε μετά την επίλυση.

[source,console]

$ git rerere diff --- a/hello.rb + b/hello.rb @@ -1,11 +1,11 @@ #! /usr/bin/env ruby

 def hello
-<<<<<<<
-  puts 'hello mundo'
-=======
+<<<<<<< HEAD
   puts 'hola world'
->>>>>>>
+=======
+  puts 'hello mundo'
+>>>>>>> i18n-world
 end
Επίσης (και αυτό στην πραγματικότητα είναι άσχετο με την `rerere`), μπορούμε να χρησιμοποιήσουμε την `ls-files -u` για να δούμε τα συγκρουόμενα αρχεία και τις εκδόσεις πρότερες, αριστερές και δεξιές εκδόσεις:

[source,console]

$ git ls-files -u 100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1 hello.rb 100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2 hello.rb 100644 54336ba847c3758ab604876419607e9443848474 3 hello.rb

Τώρα μπορούμε να επιλύσουμε τη σύγκρουση (ας πούμε ότι αποφασίζουμε να είναι `puts 'hola mundo'`) και μπορούμε να εκτελέσουμε ξανά την εντολή `rerere diff` για να δούμε τι θα θυμάται η `rerere`:

[source,console]

$ git rerere diff --- a/hello.rb + b/hello.rb @@ -1,11 +1,7 @@ #! /usr/bin/env ruby

 def hello
-<<<<<<<
-  puts 'hello mundo'
-=======
-  puts 'hola world'
->>>>>>>
+  puts 'hola mundo'
 end
Αυτό ουσιαστικά λέει ότι όταν το Git βλέπει μια σύγκρουση τμήματος κώδικα σε ένα αρχείο `hello.rb` που έχει `hello mundo` στη μία πλευρά και `hola world` στην άλλη, θα την επιλύσει διαλέγοντας `hola mundo`.

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

[source,console]

$ git add hello.rb $ git commit Recorded resolution for hello.rb. [master 68e16e5] Merge branch i18n

Βλέπουμε ότι αναφέρει `Recorded resolution for FILE`.

image::images/rerere2.png[]

Τώρα ας αναιρέσουμε αυτήν τη συγχώνευση και στη συνέχεια ας αλλάξουμε τη βάση του θεματικού κλάδου στον κύριο κλάδο.
Μπορούμε να επαναφέρουμε τον κλάδο μας χρησιμοποιώντας την `reset` όπως είδαμε στην ενότητα <<r_git_reset>>.

[source,console]

$ git reset --hard HEAD^ HEAD is now at ad63f15 i18n the hello

Η συγχώνευσή μας αναιρέθηκε.
Τώρα ας επαντοποθετήσουμε τον θεματικό κλάδο.

[source,console]

$ git checkout i18n-world Switched to branch i18n-world

$ git rebase master First, rewinding head to replay your work on top of it…​ Applying: i18n one word Using index info to reconstruct a base tree…​ Falling back to patching base and 3-way merge…​ Auto-merging hello.rb CONFLICT (content): Merge conflict in hello.rb Resolved hello.rb using previous resolution. Failed to merge in the changes. Patch failed at 0001 i18n one word

Τώρα, πήραμε την ίδια σύγκρουση συγχώνευσης όπως αναμέναμε, αλλά επιπλέον υπάρχει η γραμμή `Resolved FILE using previous resolution`.
Αν εξετάσουμε το αρχείο, θα δούμε ότι έχει ήδη επιλυθεί, δεν υπάρχουν επισημάνσεις σύγκρουσης συγχώνευσης.

[source,console]

$ cat hello.rb #! /usr/bin/env ruby

def hello puts hola mundo end

Επίσης, η `git diff` θα μας δείξει πώς ξανα-επιλύθηκε αυτόματα:

[source,console]

$ git diff diff --cc hello.rb index a440db6,54336ba..0000000 --- a/hello.rb + b/hello.rb @@@ -1,7 -1,7 +1,7 @@@ #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end
image::images/rerere3.png[]

Μπορούμε επίσης να ξαναδημιουργήσουμε την κατάσταση σύγκρουσης του αρχείου με την εντολή `checkout`:

[source,console]

$ git checkout --conflict=merge hello.rb $ cat hello.rb #! /usr/bin/env ruby

def hello <<<<<<< ours puts hola world

  puts 'hello mundo'
>>>>>>> theirs
end
Είδαμε ένα παράδειγμα αυτού στην ενότητα <<r_advanced_merging>>.
Προς το παρόν, όμως, ας την ξαναεπιλύσουμε τρέχοντας την `rerere` ξανά:

[source,console]

$ git rerere Resolved hello.rb using previous resolution. $ cat hello.rb #! /usr/bin/env ruby

def hello puts hola mundo end

Έχουμε ξαναεπιλύσει το αρχείο αυτόματα χρησιμοποιώντας την αποθηκευμένη επίλυση `rerere`.
Τώρα μπορούμε να τρέξουμε `add` και να συνεχίσουμε την αλλαγή βάσης για να την ολοκληρώσουμε.

[source,console]

$ git add hello.rb $ git rebase --continue Applying: i18n one word

Έτσι, εάν κάνουμε πολλές συγχωνεύσεις ή θέλουμε o θεματικός κλάδος μας να συμβαδίζει με τον κύριο κλάδο μας χωρίς να έχουμε πάμπολλες συγχωνεύσεις, μπορούμε είτε να κάνουμε συχνά αλλαγές βάσης, είτε να ενεργοποιήσουμε την `rerere` για να κάνουμε πιο εύκολη τη ζωή μας.



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

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

[[r_file_annotation]]
==== Επισημείωση αρχείων

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

[source,console]

$ 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`, μπορούμε να δούμε από πού προήλθαν τμήματα του κώδικα:

[source,console]

$ 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 μάς λέει την αρχική υποβολή στην οποία γράψαμε αυτές τις γραμμές ακόμα κι αν ήταν σε άλλο αρχείο.


[[r_binary_search]]
==== Δυαδική αναζήτηση

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

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

[source,console]

$ 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` και συνεχίζουμε την αναζήτηση του σφάλματος:

[source,console]

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

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

[source,console]

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

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

[source,console]

$ 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` στο σημείο που ήμασταν πριν ξεκινήσουμε αλλιώς θα καταλήξουμε σε μια περίεργη κατάσταση:

[source,console]

$ git bisect reset

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

[source,console]

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

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


[[r_git_submodules]]
=== Λειτουργικές υπομονάδες

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

Ακολουθεί ένα παράδειγμα.
Ας υποθέσουμε ότι αναπτύσσουμε έναν ιστότοπο και δημιουργούμε τροφοδοσίες Atom.
Αντί να γράφουμε τον δικό μας κώδικα δημιουργίας Atom, αποφασίζουμε να χρησιμοποιήσουμε μια βιβλιοθήκη.
Είναι πιθανό να χρειάζεται είτε να συμπεριλάβουμε αυτόν τον κώδικα από μια κοινόχρηστη βιβλιοθήκη όπως μια εγκατάσταση CPAN ή πετράδι Ruby είτε να αντιγράψουμε τον πηγαίο κώδικα στο δικό μας δέντρο έργου.
Το πρόβλημα με τη συμπερίληψη της βιβλιοθήκης είναι ότι είναι δύσκολο να προσαρμόσουμε τη βιβλιοθήκη με οποιονδήποτε τρόπο και συχνά πιο δύσκολο να την αναπτύξουμε, επειδή πρέπει να βεβαιωθούμε ότι κάθε πελάτης διαθέτει αυτήν τη βιβλιοθήκη.
Το πρόβλημα με την ενσωμάτωση του κώδικα στο δικό μας έργο είναι ότι τυχόν εξατομικευμένες αλλαγές που κάνουμε είναι δύσκολο να συγχωνευθούν, όταν υπάρχουν διαθέσιμες αλλαγές στο upstream.

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

[[r_starting_submodules]]
==== Λειτουργικές υπομονάδες: τα βασικά

Θα δούμε βήμα-βήμα την ανάπτυξη ενός απλού έργου που έχει διασπαστεί σε ένα κύριο έργο και σε μερικά υπο-έργα.

Ας αρχίσουμε προσθέτοντας ένα υπάρχον αποθετήριο Git ως λειτουργική υπομονάδα του αποθετηρίου στο οποίο εργαζόμαστε.
Για να προσθέσουμε μία νέα υπομονάδα χρησιμοποιούμε την εντολή `git submodule add` με την απόλυτη ή σχετική διεύθυνση URL του έργου που θέλουμε να αρχίσουμε να παρακολουθούμε.
Σε αυτό το παράδειγμα, θα προσθέσουμε μια βιβλιοθήκη που ονομάζεται ``DbConnector''.

[source,console]

$ git submodule add https://github.com/chaconinc/DbConnector Cloning into DbConnector…​ remote: Counting objects: 11, done. remote: Compressing objects: 100% (10/10), done. remote: Total 11 (delta 0), reused 11 (delta 0) Unpacking objects: 100% (11/11), done. Checking connectivity…​ done.

Εκ προεπιλογής, οι υπομονάδες θα προσθέσουν το υποέργο σε έναν κατάλογο που ονομάζεται το ίδιο με τον αποθετήριο, στην περίπτωση αυτή ``DbConnector''.
Μπορούμε να προσθέσουμε μια διαφορετική διαδρομή στο τέλος της εντολής, εάν θέλουμε να πάει αλλού.

Εάν εκτελέσουμε την `git status` σε αυτό το σημείο, θα παρατηρήσουμε μερικά πράγματα.

[source,console]

$ git status On branch master Your branch is up-to-date with origin/master.

Changes to be committed: (use "git reset HEAD <file>…​" to unstage)

new file:   .gitmodules
new file:   DbConnector
Πρώτα θα πρέπει να παρατηρήσουμε το νέο αρχείο .gitmodules.
Αυτό είναι ένα αρχείο διαμόρφωσης που αποθηκεύει την αντιστοίχιση μεταξύ της διεύθυνσης URL του έργου και του τοπικού υποκαταλόγου στον οποίο το έχουμε έλξει:

[source,console]

$ cat .gitmodules

path = DbConnector
url = https://github.com/chaconinc/DbConnector
Εάν έχουμε πολλές υπομονάδες, θα έχουμε πολλαπλές καταχωρήσεις σε αυτό το αρχείο.
Είναι σημαντικό να σημειωθεί ότι η έκδοση και αυτού του αρχείου ελέγχεται όπως και τα άλλα αρχεία ή το αρχείο .gitignore.
Ωθείται και έλκεται όπως και το υπόλοιπο του έργο μας.
Αυτός είναι ο τρόπος με τον οποίο αυτοί που κλωνοποιούν αυτό το έργο γνωρίζουν από πού να πάρουν τα έργα υπομονάδων.

[NOTE]
=====
Δεδομένου ότι η διεύθυνση URL στο αρχείο .gitmodules είναι αυτή που θα προσπαθήσουν πρώτα να κλωνοποιήσουν/ανακτήσουν οι άλλοι χρήστες, πρέπει να βεβαιωθούμε ότι χρησιμοποιούμε μια διεύθυνση URL στην οποιία έχουν  πρόσβαση, εάν είναι δυνατόν.
Για παράδειγμα, αν ωθούμε σε διαφορετική διεύθυνση URL από αυτήν από την οποία έλκουν οι άλλοι, καλό είναι να χρησιμοποιούμε αυτήν στην οποία έχουν πρόσβαση άλλοι.
Μπορούμε να αντικαταστήσουμε αυτήν την τιμή τοπικά με την `git config submodule.DbConnector.url PRIVATE_URL` για δική μας χρήση.
Όταν είναι εφικτό, μια σχετική διεύθυνση URL μπορεί να είναι επίσης χρήσιμη.
=====

Η άλλη λίστα στην έξοδο της `git status` είναι η καταχώρηση του καταλόγου του έργου.
Εάν εκτελέσουμε την `git diff` σε αυτήν, βλέπουμε κάτι ενδιαφέρον:

[source,console]

$ git diff --cached DbConnector diff --git a/DbConnector b/DbConnector new file mode 160000 index 0000000..c3f01dc --- /dev/null + b/DbConnector @@ -0,0 +1 @@ +Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc

Αν και `DbConnector` είναι ένας υποκατάλογος στον κατάλογο εργασίας μας, το Git τον βλέπει ως υπομονάδα και δεν παρακολουθεί τα περιεχόμενά του όταν δεν βρισκόμαστε σε αυτόν τον κατάλογο.
Αντίθετα το Git τον βλέπει ως μια συγκεκριμένη υποβολή από αυτό το αποθετήριο.

Αν θέλουμε λίγο καλύτερη έξοδο diff, μπορούμε να περάσουμε την επιλογή `--submodule` στην `git diff`.

[source,console]

$ git diff --cached --submodule diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..71fc376 --- /dev/null + b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "DbConnector"] + path = DbConnector + url = https://github.com/chaconinc/DbConnector Submodule DbConnector 0000000…​c3f01dc (new submodule)

Όταν υποβάλλουμε, βλέπουμε κάτι τέτοιο:

[source,console]

$ git commit -am added DbConnector module [master fb9093c] added DbConnector module 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 DbConnector

Παρατηρούμε τη λειτουργία `160000` για την καταχώρηση `DbConnector`.
Αυτή είναι μια ειδική λειτουργία στο Git που ουσιαστικά σημαίνει ότι καταγράφουμε μια υποβολή ως καταχώρηση καταλόγου και όχι ως υποκατάλογο ή αρχείο.

[[r_cloning_submodules]]
==== Κλωνοποίηση έργου με υπομονάδες

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

[source,console]

$ git clone https://github.com/chaconinc/MainProject Cloning into MainProject…​ remote: Counting objects: 14, done. remote: Compressing objects: 100% (13/13), done. remote: Total 14 (delta 1), reused 13 (delta 0) Unpacking objects: 100% (14/14), done. Checking connectivity…​ done. $ cd MainProject $ ls -la total 16 drwxr-xr-x 9 schacon staff 306 Sep 17 15:21 . drwxr-xr-x 7 schacon staff 238 Sep 17 15:21 .. drwxr-xr-x 13 schacon staff 442 Sep 17 15:21 .git -rw-r—​r-- 1 schacon staff 92 Sep 17 15:21 .gitmodules drwxr-xr-x 2 schacon staff 68 Sep 17 15:21 DbConnector -rw-r—​r-- 1 schacon staff 756 Sep 17 15:21 Makefile drwxr-xr-x 3 schacon staff 102 Sep 17 15:21 includes drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 scripts drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 src $ cd DbConnector/ $ ls $

Ο κατάλογος `DbConnector` είναι εκεί, αλλά κενός.
Πρέπει να εκτελέσουμε δύο εντολές: την `git submodule init` για να  αρχικοποιήσουμε το τοπικό μας αρχείο διαμόρφωσης και την `git submodule update` για να ανακτήσουμε όλα τα δεδομένα από το έργο και να κάνουμε μεταβούμε στην κατάλληλη υποβολή που αναφέρεται στο υπερ-έργο μας:

[source,console]

$ git submodule init Submodule DbConnector (https://github.com/chaconinc/DbConnector) registered for path DbConnector $ git submodule update Cloning into DbConnector…​ remote: Counting objects: 11, done. remote: Compressing objects: 100% (10/10), done. remote: Total 11 (delta 0), reused 11 (delta 0) Unpacking objects: 100% (11/11), done. Checking connectivity…​ done. Submodule path DbConnector: checked out c3f01dc8862123d317dd46284b05b6892c7b29bc

Τώρα ο υποκατάλογος `DbConnector` βρίσκεται στην ακριβή κατάσταση που ήταν όταν υποβάλλαμε νωρίτερα.

Υπάρχει ένας άλλος τρόπος για να γίνει αυτό, ο οποίο όμως είναι μάλιστα λίγο πιο απλός.
Αν περάσουμε την επιλογή `--recursive` στην εντολή `git clone`, θα αρχικοποιήσει και ενημερώσει κάθε υπομονάδα στο αποθετήριο αυτόματα.

[source,console]

$ git clone --recursive https://github.com/chaconinc/MainProject Cloning into MainProject…​ remote: Counting objects: 14, done. remote: Compressing objects: 100% (13/13), done. remote: Total 14 (delta 1), reused 13 (delta 0) Unpacking objects: 100% (14/14), done. Checking connectivity…​ done. Submodule DbConnector (https://github.com/chaconinc/DbConnector) registered for path DbConnector Cloning into DbConnector…​ remote: Counting objects: 11, done. remote: Compressing objects: 100% (10/10), done. remote: Total 11 (delta 0), reused 11 (delta 0) Unpacking objects: 100% (11/11), done. Checking connectivity…​ done. Submodule path DbConnector: checked out c3f01dc8862123d317dd46284b05b6892c7b29bc

==== Εργασία σε έργο με υπομονάδες

Τώρα έχουμε αντίγραφο ενός έργου με υπομονάδες και θα δουλέψουμε με τους συνεργάτες μας τόσο για το κύριο έργο όσο και για τα έργα των υπομονάδων.

===== Έλξη των αλλαγών

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

Αν θέλουμε να ελέγξουμε για νέα εργασία σε μία υπομονάδα, μπορούμε να μεταβούμε στον κατάλογο και να εκτελέσουμε `git fetch` και `git merge` για να ενημερώσουμε τον τοπικό κώδικα.

[source,console]

$ git fetch From https://github.com/chaconinc/DbConnector c3f01dc..d0354fc master → origin/master $ git merge origin/master Updating c3f01dc..d0354fc Fast-forward scripts/connect.sh | 1
src/db.c | 1
2 files changed, 2 insertions(+)

Τώρα, αν επιστρέψουμε στο κύριο έργο και εκτελέσουμε την `git diff --submodule`, μπορούμε να δούμε ότι η υπομονάδα ενημερώθηκε και να πάρουμε μια λίστα υποβολών που προστέθηκαν σε αυτήν.
Εάν δεν θέλουμε να πληκτρολογούμε `--submodule` κάθε φορά που τρέχουμε την `git diff`, μπορούμε να την ορίσουμε ως προεπιλεγμένη μορφή ρυθμίζοντας την τιμή της `diff.submodule` στο `log`.

[source,console]

$ git config --global diff.submodule log $ git diff Submodule DbConnector c3f01dc..d0354fc: > more efficient db routine > better connection routine

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

Πάλι υπάρχει ένας ευκολότερος τρόπος για να γίνει αυτό, αν προτιμάμε να μην ανακτούμε και συγχωνεύουμε με μη αυτόματο τρόπο τον υποκατάλογο.
Εάν εκτελέσουμε την `git submodule update --remote`, το Git θα μεταβεί στις υπομονάδες μας, θα ανακτήσει και θα ενημερώσει για μας.

[source,console]

$ git submodule update --remote DbConnector remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector 3f19983..d0354fc master → origin/master Submodule path DbConnector: checked out d0354fc054692d3906c85c3af05ddce39a1c0644

Αυτή η εντολή θα υποθέσει εκ προεπιλογής ότι θέλουμε να ενημερώσουμε τον κλάδο `master` του αποθετηρίου υπομονάδων.
Ωστόσο, μπορούμε να το ορίσουμε να είναι κάτι διαφορετικό, εφόσον το θέλουμε.
Για παράδειγμα, εάν θέλουμε η υπομονάδα DbConnector να παρακολουθεί τον κλάδο `stable` του αποθετηρίου, μπορούμε να το ορίσουμε είτε στο αρχείο `.gitmodules`, είτε στο τοπικό αρχείο `.git/config`.
Ας το ορίσουμε στο αρχείο `.gitmodules`:

[source,console]

$ git config -f .gitmodules submodule.DbConnector.branch stable

$ git submodule update --remote remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector 27cf5d3..c87d55d stable → origin/stable Submodule path DbConnector: checked out c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687

Αν παραλείψουμε την επιλογή `-f .gmodmodules`, θα κάνει μόνο την αλλαγή για μας, αλλά πιθανότατα είναι πιο λογικό να παρακολουθούμε αυτές τις πληροφορίες με το αποθετήριο, ώστε και όλοι οι άλλοι να το κάνουν.

Αν τρέξουμε `git status` σε αυτό το σημείο, το Git θα μας δείξει ότι έχουμε ``νέες υποβολές'' στην υπομονάδα.

[source,console]

$ git status On branch master Your branch is up-to-date with origin/master.

Changes not staged for commit: (use "git add <file>…​" to update what will be committed) (use "git checkout — <file>…​" to discard changes in working directory)

modified:   .gitmodules
modified:   DbConnector (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

Εάν ορίσουμε τη ρύθμιση διαμόρφωσης `status.submodulesummary`, το Git θα μας δείξει επίσης μια σύντομη περίληψη των αλλαγών στις υπομονάδες μας:

[source,console]

$ git config status.submodulesummary 1

$ git status On branch master Your branch is up-to-date with origin/master.

Changes not staged for commit: (use "git add <file>…​" to update what will be committed) (use "git checkout — <file>…​" to discard changes in working directory)

modified:   .gitmodules
modified:   DbConnector (new commits)

Submodules changed but not updated:

  • DbConnector c3f01dc…​c87d55d (4): > catch non-null terminated lines

Σε αυτό το σημείο, αν εκτελέσουμε την `git diff`, μπορούμε να δούμε τόσο ότι έχουμε τροποποιήσει το αρχείο `.gitmodules` όσο και ότι υπάρχουν αρκετές υποβολές που έχουμε έλξει και είμαστε έτοιμοι να υποβάλλουμε για στην υπομονάδα του έργου μας.

[source,console]

$ git diff diff --git a/.gitmodules b/.gitmodules index 6fc0b3d..fd1cc29 100644 --- a/.gitmodules + b/.gitmodules @@ -1,3 +1,4 @@ [submodule "DbConnector"] path = DbConnector url = https://github.com/chaconinc/DbConnector + branch = stable Submodule DbConnector c3f01dc..c87d55d: > catch non-null terminated lines > more robust error handling > more efficient db routine > better connection routine

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

[source,console]

$ git log -p --submodule commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae Author: Scott Chacon <schacon@gmail.com> Date: Wed Sep 17 16:37:02 2014 +0200

updating DbConnector for bug fixes

diff --git a/.gitmodules b/.gitmodules index 6fc0b3d..fd1cc29 100644 --- a/.gitmodules + b/.gitmodules @@ -1,3 +1,4 @@ [submodule "DbConnector"] path = DbConnector url = https://github.com/chaconinc/DbConnector + branch = stable Submodule DbConnector c3f01dc..c87d55d: > catch non-null terminated lines > more robust error handling > more efficient db routine > better connection routine

Το Git θα προσπαθήσει εκ προεπιλογής να ενημερώσει *όλες* τις υπομονάδες μας όταν τρέχουμε την `git submodule update --remote`, οπότε αν έχουμε πολλές από δαύτες, ίσως θέλουμε να περάσουμε το όνομα μόνο της υπομονάδας που θέλουμε να ενημερώσουμε.

===== Εργασία σε υπομονάδα

Είναι πολύ πιθανό ότι αν χρησιμοποιούμε υπομονάδες, το κάνουμε επειδή θέλουμε να εργαστούμε στον κώδικα στην υπομονάδα και ταυτόχρονα εργαζόμαστε στον κώδικα στο κύριο έργο (ή σε πολλές υπομονάδες).
Διαφορετικά, πιθανότατα θα χρησιμοποιούσαμε ένα απλούστερο σύστημα διαχείρισης εξαρτήσεων (όπως τα Maven ή Rubygems).

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

Μέχρι στιγμής, όταν εκτελούσαμε την εντολή `git submodule update` για να ανακτήσουμε τις αλλαγές από τα αποθετήρια υπομονάδων, το Git έπαιρνε τις αλλαγές και ενημέρωνε τα αρχεία στον υποκατάλογο, αλλά άφηνε το υπό-αποθετήριο σε μία κατάσταση που ονομάζεται κατάσταση με αποσπασμένο  HEAD (``detached HEAD'').
Αυτό σημαίνει ότι δεν υπάρχει τοπικός κλάδος εργασίας (όπως ο `master` για παράδειγμα) που παρακολουθεί τις αλλαγές.
Επομένως, οι όποιες αλλαγές κάνουμε δεν παρακολουθούνται καλά.

Προκειμένου να ρυθμίσουμε την υπομονάδα μας ώστε να είναι ευκολότερο να μπούμε και να την τροποποίησουμε, πρέπει να κάνουμε δύο πράγματα.
Πρέπει να πάμε σε κάθε υπομονάδα και να μεταβούμε σε έναν κλάδο στον οποίο θα εργαστούμε.
Στη συνέχεια πρέπει να πούμε στο Git τι να κάνει, αν έχουμε κάνει αλλαγές και μετά η `git submodule update --remote` έλκει όποια καινούρια δουλειά που έγινε στο upstream.
Οι επιλογές είναι ότι μπορούμε να τις συγχωνεύσουμε στην τοπική δουλειά μας ή να δοκιμάσουμε να αλλάξουμε τη βάση της τοπικής εργασίας μας στις νέες αλλαγές.

Καταρχάς, ας πάμε στον κατάλογο υπομονάδων μας και ας μεταβούμε σε κάποιον κλάδο.

[source,console]

$ git checkout stable Switched to branch stable

Ας το δοκιμάσουμε με την επιλογή `--merge`.
Για να το καθορίσουμε με το χέρι, μπορούμε απλά να προσθέσουμε την επιλογή `--merge` στην κλήση της `update'.
Εδώ θα δούμε ότι υπήρξε μια αλλαγή στον διακομιστή για αυτην την υπομονάδα και συγχωνεύεται.

[source,console]

$ git submodule update --remote --merge remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector c87d55d..92c7337 stable → origin/stable Updating c87d55d..92c7337 Fast-forward src/main.c | 1
1 file changed, 1 insertion(+) Submodule path DbConnector: merged in 92c7337b30ef9e0893e758dac2459d07362ab5ea

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

[source,console]

$ cd DbConnector/ $ vim src/db.c $ git commit -am unicode support [stable f906e16] unicode support 1 file changed, 1 insertion(+)

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

[source,console]

$ git submodule update --remote --rebase First, rewinding head to replay your work on top of it…​ Applying: unicode support Submodule path DbConnector: rebased into 5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94

Εάν ξεχάσουμε τις επιλογές `--rebase` ή `--merge`, το Git απλά θα ενημερώσει την υπομονάδα σε ό,τι βρίσκεται στον διακομιστή και θα επαναφέρει το έργο μας σε κατάσταση αποσπασμένου HEAD.

[source,console]

$ git submodule update --remote Submodule path DbConnector: checked out 5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94

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

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

[source,console]

$ git submodule update --remote remote: Counting objects: 4, done. remote: Compressing objects: 100% (3/3), done. remote: Total 4 (delta 0), reused 4 (delta 0) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector 5d60ef9..c75e92a stable → origin/stable error: Your local changes to the following files would be overwritten by checkout: scripts/setup.sh Please, commit your changes or stash them before you can switch branches. Aborting Unable to checkout c75e92a2b3855c9e5b66f915308390d9db204aca in submodule path DbConnector

Εάν κάναμε αλλαγές που συγκρούνται με κάτι που άλλαξε στο upstream, το Git θα μας ενημερώσει όταν εκτελούμε την ενημέρωση.

[source,console]

$ git submodule update --remote --merge Auto-merging scripts/setup.sh CONFLICT (content): Merge conflict in scripts/setup.sh Recorded preimage for scripts/setup.sh Automatic merge failed; fix conflicts and then commit the result. Unable to merge c75e92a2b3855c9e5b66f915308390d9db204aca in submodule path DbConnector

Μπορούμε να μεταβούμε στον κατάλογο υπομονάδων και να διορθώσουμε τη σύγκρουση όπως θα κάναμε κανονικά.

[[r_publishing_submodules]]
===== Δημοσίευση αλλαγών σε υπομονάδες

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

[source,console]

$ git diff Submodule DbConnector c87d55d..82d2ad3: > Merge from origin/stable > updated setup script > unicode support > remove unnecessary method > add new option for conn pooling

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

Για να βεβαιωθούμε ότι αυτό δεν συμβαίνει, μπορούμε να ζητήσουμε από το Git να ελέγξει ότι όλες οι υπομοναδες μας έχουν ωθηθεί σωστά πριν ωθήσουμε στο κύριο έργο.
Η εντολή `git push` παίρνει το όρισμα `--recurse-submodules` το οποίο μπορεί να τεθεί είτε σε `check` είτε σε `on-demand`.
Η επιλογή `check` θα κάνει την `push` να αποτύχει εάν κάποια από τις υποβαλλόμενες αλλαγές της υπομονάδας δεν έχει ωθηθεί.

[source,console]

$ git push --recurse-submodules=check The following submodule paths contain changes that can not be found on any remote: DbConnector

Please try

git push --recurse-submodules=on-demand

or cd to the path and use

git push

to push them to a remote.

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

Η άλλη επιλογή είναι να χρησιμοποιήσουμε την τιμή `on-demand`, η οποία θα προσπαθήσει να το κάνει αντί για μας.

[source,console]

$ git push --recurse-submodules=on-demand Pushing submodule DbConnector Counting objects: 9, done. Delta compression using up to 8 threads. Compressing objects: 100% (8/8), done. Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done. Total 9 (delta 3), reused 0 (delta 0) To https://github.com/chaconinc/DbConnector c75e92a..82d2ad3 stable → stable Counting objects: 2, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (2/2), 266 bytes | 0 bytes/s, done. Total 2 (delta 1), reused 0 (delta 0) To https://github.com/chaconinc/MainProject 3d6d338..9a377d1 master → master

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

===== Συγχώνευση αλλαγών υπομονάδων

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

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

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

[source,console]

$ git pull remote: Counting objects: 2, done. remote: Compressing objects: 100% (1/1), done. remote: Total 2 (delta 1), reused 2 (delta 1) Unpacking objects: 100% (2/2), done. From https://github.com/chaconinc/MainProject 9a377d1..eb974f8 master → origin/master Fetching submodule DbConnector warning: Failed to merge submodule DbConnector (merge following commits not found) Auto-merging DbConnector CONFLICT (submodule): Merge conflict in DbConnector Automatic merge failed; fix conflicts and then commit the result.

Έτσι, βασικά, αυτό που συνέβη εδώ είναι ότι το Git έχει καταλάβει ότι οι δύο κλάδοι καταγράφουν σημεία στο ιστορικό της υπομονάδας που είναι αποκλίνοντα και πρέπει να συγχωνευθούν.
Το εξηγεί ως ``δεν βρέθηκε συγχώνευση μετά τις υποβολές'' (``merge following commits not found''), που μπερδεύει λιγάκι, αλλά θα εξηγήσουμε για ποιον λόγο σε λίγο.

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

[source,console]

$ git diff diff --cc DbConnector index eb41d76,c771610..0000000 --- a/DbConnector + b/DbConnector

Έτσι, στην περίπτωση αυτή, `eb41d76` είναι η υποβολή στην υπομονάδα μας που *εμείς* είχαμε και `c771610` είναι η υποβολή που υπήρχε στο upstream.
Αν πάμε στον κατάλογο υπομονάδων μας, θα πρέπει να είναι ήδη στην `eb41d76` καθώς η συγχώνευση δεν τον άγγιξε.
Εάν για οποιονδήποτε λόγο δεν είναι, μπορούμε απλά να δημιουργήσουμε έναν κλάδο που δείχνει σε αυτήν και να μεταβούμε σε αυτόν.

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

Έτσι, θα πάμε στον κατάλογο υπομονάδων μας, θα δημιουργήσουμε έναν κλάδο βασισμένοι σε αυτόν τον δεύτερο αριθμό SHA-1 από την `git diff` και θα συγχωνεύσουμε με το χέρι.

[source,console]

$ cd DbConnector

$ git rev-parse HEAD eb41d764bccf88be77aced643c13a7fa86714135

$ git branch try-merge c771610 (DbConnector) $ git merge try-merge Auto-merging src/main.c CONFLICT (content): Merge conflict in src/main.c Recorded preimage for src/main.c Automatic merge failed; fix conflicts and then commit the result.

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

[source,console]

$ vim src/main.c <1> $ git add src/main.c $ git commit -am merged our changes Recorded resolution for src/main.c. [master 9fd905e] merged our changes

$ cd .. <2> $ git diff <3> diff --cc DbConnector index eb41d76,c771610..0000000 --- a/DbConnector + b/DbConnector @@@ -1,1 -1,1 +1,1 @@@ - Subproject commit eb41d764bccf88be77aced643c13a7fa86714135 -Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a $ git add DbConnector <4>

$ git commit -m "Merge Tom’s Changes" <5> [master 10d2c60] Merge Tom’s Changes

<1> Επιλύουμε τη σύγκρουση
<2> Επιστρέφουμε στον κατάλογο του κύριου έργου
<3> Ελέγχουμε τους SHA-1 ξανά
<4> Επιλύουμε τη συγκρουόμενη καταχώρηση της υπομονάδας
<5> Υποβάλλουμε τη συγχώνευσή μας

Μπορεί να φαίνεται λίγο συγκεχυμένη, αλλά δεν είναι πραγματικά πολύ δύσκολο.

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

Αυτός είναι ο λόγος για τον οποίο το μήνυμα λάθους από πριν ήταν ``δεν βρέθηκε συγχώνευση μετά τις υποβολές'', επειδή δεν μπορούσε να κάνει *αυτό*.
Δημιουργεί σύγχυση διότι ποιος θα περίμενε *να προσπαθήσει* να το κάνει κάτι τέτοιο;

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

[source,console]

$ git merge origin/master warning: Failed to merge submodule DbConnector (not fast-forward) Found a possible merge resolution for the submodule: 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: > merged our changes If this is correct simply add it to the index for example by using:

git update-index --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a "DbConnector"

which will accept this suggestion. Auto-merging DbConnector CONFLICT (submodule): Merge conflict in DbConnector Automatic merge failed; fix conflicts and then commit the result.

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

[source,console]

$ cd DbConnector/ $ git merge 9fd905e Updating eb41d76..9fd905e Fast-forward

$ cd .. $ git add DbConnector $ git commit -am Fast forwarded to a common submodule child

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

==== Συμβουλές για υπομονάδες

Υπάρχουν μερικά πράγματα που μπορούμε να κάνουμε για να κάνουμε την εργασία με τις υπομονάδες λίγο πιο εύκολη.

===== `submodule foreach`

Υπάρχει μια εντολή `submodule foreach` για να εκτελέσουμε μία οποιαδήποτε εντολή σε κάθε υπομονάδα.
Αυτό μπορεί να είναι πραγματικά χρήσιμο εάν έχουμε πολλές υπομονάδες στο ίδιο έργο.

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

[source,console]

$ git submodule foreach git stash Entering CryptoLibrary No local changes to save Entering DbConnector Saved working directory and index state WIP on stable: 82d2ad3 Merge from origin/stable HEAD is now at 82d2ad3 Merge from origin/stable

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

[source,console]

$ git submodule foreach git checkout -b featureA Entering CryptoLibrary Switched to a new branch featureA Entering DbConnector Switched to a new branch featureA

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

[source,console]

$ git diff; git submodule foreach git diff Submodule DbConnector contains modified content diff --git a/src/main.c b/src/main.c index 210f1ae..1f0acdc 100644 --- a/src/main.c + b/src/main.c @@ -245,6 +245,8 @@ static int handle_alias(int argcp, const char **argv)

commit_pager_choice();

+ url = url_decode(url_orig);

+ /* build alias_argv */ alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); alias_argv[0] = alias_string + 1; Entering DbConnector diff --git a/src/db.c b/src/db.c index 1aaefb6..5297645 100644 --- a/src/db.c + b/src/db.c @@ -93,6 +93,11 @@ char *url_decode_mem(const char *url, int len) return url_decode_internal(&url, len, NULL, &out, 0); }

+char *url_decode(const char *url) +{ + return url_decode_mem(url, strlen(url)); +}

+ char *url_decode_parameter_name(const char **query) { struct strbuf out = STRBUF_INIT;

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


===== Χρήσιμα ψευδώνυμα

Μπορεί να θέλουμε να ορίσουμε ορισμένα ψευδώνυμα (aliases) για ορισμένες από αυτές τις εντολές, καθώς μπορεί να είναι αρκετά μακροσκελείς και για τις περισσότερες από αυτές δεν μπορούμε να ορίσουμε επιλογές διαμόρφωσης για να τις καταστήσουμε προεπιλεγμένες.
Καλύψαμε τη δημιουργία των ψευδωνύμων του Git στην ενότητα <<r_git_aliases>>, αλλά εδώ είναι ένα παράδειγμα του τι μπορεί να θέλουμε να ρυθμίσουμε εάν σκοπεύουμε να δουλεύουμε συχνά με υπομονάδες στο Git.

[source,console]

$ git config alias.sdiff !"git diff && git submodule foreach git diff" $ git config alias.spush push --recurse-submodules=on-demand $ git config alias.supdate submodule update --remote --merge

Με αυτό τον τρόπο μπορούμε απλά να εκτελέσουμε την `git supdate` όταν θέλουμε να ενημερώσουμε τις υπομονάδες μας ή την `git spush` για να ωθήσουμε τον έλεγχο εξαρτήσεων των υπομονάδων.

==== Προβλήματα με τις υπομονάδες

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

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

[source,console]

$ git checkout -b add-crypto Switched to a new branch add-crypto

$ git submodule add https://github.com/chaconinc/CryptoLibrary Cloning into CryptoLibrary…​ …​

$ git commit -am adding crypto library [add-crypto 4445836] adding crypto library 2 files changed, 4 insertions(+) create mode 160000 CryptoLibrary

$ git checkout master warning: unable to rmdir CryptoLibrary: Directory not empty Switched to branch master Your branch is up-to-date with origin/master.

$ git status On branch master Your branch is up-to-date with origin/master.

Untracked files: (use "git add <file>…​" to include in what will be committed)

CryptoLibrary/

nothing added to commit but untracked files present (use "git add" to track)

Η κατάργηση του καταλόγου δεν είναι δύσκολη, αλλά μπορεί να μας μπερδέψει λίγο η παρουσία του.
Αν τον διαγράψουμε και μετά επιστρέψουμε στον κλάδο που έχει αυτήν την υπομονάδα, θα χρειαστεί να εκτελέσουμε την `submodule update --init` για να τον επαναοικίσουμε.

[source,console]

$ git clean -fdx Removing CryptoLibrary/

$ git checkout add-crypto Switched to branch add-crypto

$ ls CryptoLibrary/

$ git submodule update --init Submodule path CryptoLibrary: checked out b8dda6aa182ea4464f3f3264b11e0268545172af

$ ls CryptoLibrary/ Makefile includes scripts src

Και πάλι, δεν είναι πραγματικά πολύ δύσκολο, αλλά μπορεί να μας μπερδέψει λίγο.

Ένα άλλο πρόβλημα, που αντιμετωπίζουν πολλοί, εμπλέκει τη μετατροπή υποκαταλόγων σε υπομονάδες.
Αν παρακολουθούμε αρχεία στο έργο μας και θέλουμε να τα μετακινήσουμε σε κάποια υπομονάδα, πρέπει να είμαστε προσεκτικοί αλλιώς το Git θα μας θυμώσει.
Ας υποθέσουμε ότι έχουμε αρχεία σε έναν υποκατάλογο του έργου μας και θέλουμε να τον μετατρέψουμε σε υπομονάδα.
Αν διαγράψουμε τον υποκατάλογο και στη συνέχεια εκτελέσουμε το `submodule add`, το Git μάς μαλώνει:

[source,console]

$ rm -Rf CryptoLibrary/ $ git submodule add https://github.com/chaconinc/CryptoLibrary CryptoLibrary already exists in the index

Πρέπει πρώτα να αφαιρέσουμε τον κατάλογο `CryptoLibrary` από το στάδιο καταχώρισης.
Κατόπιν μπορούμε να προσθέσουμε την υπομονάδα:

[source,console]

$ git rm -r CryptoLibrary $ git submodule add https://github.com/chaconinc/CryptoLibrary Cloning into CryptoLibrary…​ remote: Counting objects: 11, done. remote: Compressing objects: 100% (10/10), done. remote: Total 11 (delta 0), reused 11 (delta 0) Unpacking objects: 100% (11/11), done. Checking connectivity…​ done.

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

[source,console]

$ git checkout master error: The following untracked working tree files would be overwritten by checkout: CryptoLibrary/Makefile CryptoLibrary/includes/crypto.h …​ Please move or remove them before you can switch branches. Aborting

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

[source,console]

$ git checkout -f master warning: unable to rmdir CryptoLibrary: Directory not empty Switched to branch master

Στη συνέχεια, όταν επιστρέφουμε, παίρνουμε έναν κενό κατάλογο `CryptoLibrary` για κάποιο λόγο και η `git submodule update` δεν μπορεί να το διορθώσει.
Ενδέχεται να χρειαστεί να μεταβούμε στον κατάλογο υπομονάδων μας και να εκτελέσουμε μία `git checkout` για να ξαναπάρουμε όλα τα αρχεία μας.
Θα μπορούσαμε να το κάνουμε αυτό με σε ένα script με `submodule foreach`, ώστε να εκτελεστεί για πολλές υπομονάδες.

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

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


[[r_bundling]]
=== Δεμάτιασμα δεδομένων

Αν και καλύψαμε τους συνήθεις τρόπους μεταφοράς των δεδομένων του Git μέσω δικτύου (HTTP, SSH κ.λπ.), υπάρχει πράγματι ένας ακόμη τρόπος να το κάνουμε αυτό που δεν χρησιμοποιείται συνήθως, αλλά μπορεί να είναι πραγματικά πολύ χρήσιμος.

Το Git είναι ικανό να ``δεματιάζει'' (bundle) τα δεδομένα του σε ένα μοναδικό αρχείο.
Αυτό μπορεί να είναι χρήσιμο σε διάφορες περιστάσεις.
Ίσως το δίκτυό μας να είναι πεσμένο και θέλουμε να στείλουμε αλλαγές στους συναδέλφους μας.
Ίσως εργαζόμαστε κάπου εξ αποστάσεως και δεν έχουμε πρόσβαση στο τοπικό δίκτυο για λόγους ασφαλείας.
Ίσως η κάρτα μας ασύρματου δικτύου / δικτύου ethernet μόλις χάλασε.
Ίσως δεν έχουμε επί του παρόντος πρόσβαση σε έναν κοινόχρηστο διακομιστή, θέλουμε να στείλουμε διορθώσεις με e-mail σε κάποιον χρήστη και δεν θέλουμε να μεταφέρουμε 40 υποβολές μέσω της `format-patch`.

Σε αυτές τις περιστάσεις η εντολή `git bundle` μπορεί να είναι χρήσιμη.
Η εντολή `bundle` πακετάρει όλα όσα θα έπρεπε κανονικά να ωθηθούν πάνω από το δίκτυο με την εντολή `git push` σε ένα δυαδικό αρχείο το οποίο μπορούμε να στείλουμε με email σε κάποιον ή να τοποθετήσουμε μια flash drive και στη συνέχεια να ξεπακετάρουμε σε κάποιο άλλο αποθετήριο.

Ας δούμε ένα απλό παράδειγμα.
Ας υποθέσουμε ότι έχουμε ένα αποθετήριο με δύο υποβολές:

[source,console]

$ git log commit 9a466c572fe88b195efd356c3f2bbeccdb504102 Author: Scott Chacon <schacon@gmail.com> Date: Wed Mar 10 07:34:10 2010 -0800

second commit

commit b1ec3248f39900d2a406049d762aa68e9641be25 Author: Scott Chacon <schacon@gmail.com> Date: Wed Mar 10 07:34:01 2010 -0800

first commit
Εάν θέλουμε να στείλουμε αυτό το αποθετήριο σε κάποιον και δεν έχουμε πρόσβαση σε κάποιο άλλο αποθετήριο για να το ωθήσουμε ή απλά δεν θέλουμε να εγκαταστήσουμε ένα, μπορούμε να το δεματιάσουμε με την `git bundle create`.

[source,console]

$ git bundle create repo.bundle HEAD master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (6/6), 441 bytes, done. Total 6 (delta 0), reused 0 (delta 0)

Τώρα έχουμε ένα αρχείο με όνομα `repo.bundle` που έχει όλα τα δεδομένα που απαιτούνται για να δημιουργηθεί εκ νέου ο κλάδος `master` του αποθετηρίου.
Στην εντολή `bundle` πρέπει να παραθέσουμε κάθε αναφορά ή συγκεκριμένο εύρος υποβολών θέλουμε να συμπεριλάβουμε.
Αν σκοπεύουμε να κλωνοποιηθεί κάπου αλλού, θα πρέπει να προσθέσουμε τον `HEAD` ως αναφορά, όπως κάναμε εδώ.

Μπορούμε να στείλουμε το αρχείο `repo.bundle` με e-mail σε κάποιον άλλο, ή να το τοποθετήσουμε σε μια μονάδα USB και να το δώσουμε.

Από την άλλη, ας πούμε ότι κάποιος μας έχει στείλει αυτό το αρχείο `repo.bundle` και θέλουμε να εργαστούμε στο έργο.
Μπορούμε να κλωνοποιήσουμε από το δυαδικό αρχείο σε έναν κατάλογο, όπως θα κάναμε από μια διεύθυνση URL.

[source,console]

$ git clone repo.bundle repo Initialized empty Git repository in /private/tmp/bundle/repo/.git/ $ cd repo $ git log --oneline 9a466c5 second commit b1ec324 first commit

Εάν δεν συμπεριλαμβάνουμε τον `HEAD` στις αναφορές, πρέπει επίσης να καθορίσουμε το `-b master` ή οποιονδήποτε κλάδο περιλαμβάνεται επειδή αλλιώς δεν θα ξέρει σε ποιον κλάδο να μεταβεί.

Τώρα ας υποθέσουμε ότι κάνουμε τρεις υποβολές σε αυτό και θέλουμε να στείλουμε τις νέες υποβολές πίσω σε ένα δεμάτι με ένα USB stick ή e-mail.

[source,console]

$ git log --oneline 71b84da last commit - second repo c99cf5b fourth commit - second repo 7011d3d third commit - second repo 9a466c5 second commit b1ec324 first commit

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

Για να γίνει αυτό, θα πρέπει να υπολογίσουμε τη διαφορά.
Όπως περιγράψαμε στην ενότητα <<r_commit_ranges>>, μπορούμε να καθορίσουμε μια σειρά υποβολών με διάφορους τρόπους.
Για να πάρουμε τις τρεις υποβολές που έχουμε στον κύριο κλάδο μας και που δεν ήταν στον κλάδο που αρχικά κλωνοποιήσαμε, μπορούμε να χρησιμοποιήσουμε κάτι σαν `origin/master..master` ή `master ^origin/master`.
Ας το δοκιμάσουμε με την εντολή `log`.

[source,console]

$ git log --oneline master ^origin/master 71b84da last commit - second repo c99cf5b fourth commit - second repo 7011d3d third commit - second repo

Τώρα, λοιπόν, που έχουμε τον κατάλογο των υποβολών που θέλουμε να συμπεριλάβουμε στο δεμάτι, ας τις δεματιάσουμε.
Αυτό το κάνουμε με την εντολή `git bundle create`, στην οποία δίνουμε το όνομα αρχείου που θέλουμε να έχει το δεμάτι μας και το εύρος των υποβολών που θέλουμε να το κάνουμε.

[source,console]

$ git bundle create commits.bundle master ^9a466c5 Counting objects: 11, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (9/9), 775 bytes, done. Total 9 (delta 0), reused 0 (delta 0)

Τώρα έχουμε ένα αρχείο `commits.bundle` στον κατάλογό μας.
Αν το πάρουμε και το στείλουμε στη συνεργάτιδα μας, τότε μπορεί να το εισάγει στο αρχικό αποθετήριο, ακόμα κι αν σε αυτό έχει γίνει περαιτέρω δουλειά εν τω μεταξύ.

Όταν παίρνει το δεμάτι, μπορεί να το επιθεωρήσει για να δει τι περιέχει πριν το εισάγει στο αποθετήριό της.
Η πρώτη εντολή είναι η εντολή `bundle verify` που θα διασφαλίσει ότι το αρχείο είναι στην πραγματικότητα ένα έγκυρο δεμάτι Git και ότι έχουμε όλους τους απαραίτητους προγόνους για να το ανασυστήσουμε σωστά.

[source,console]

$ git bundle verify ../commits.bundle The bundle contains 1 ref 71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master The bundle requires these 1 ref 9a466c572fe88b195efd356c3f2bbeccdb504102 second commit ../commits.bundle is okay

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

[source,console]

$ git bundle verify ../commits-bad.bundle error: Repository lacks these prerequisite commits: error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 third commit - second repo

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

[source,console]

$ git bundle list-heads ../commits.bundle 71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master

Η υπο-εντολή `verify` επίσης θα μας πει τις κεφαλές.
Ο σκοπός είναι να μπορούμε να δούμε τι μπορούμε να έλξουμε, ώστε να μπορούμε να χρησιμοποιήσουμε τις εντολές `fetch` ή `pull` για να εισάγουμε υποβολές από αυτό το δεμάτι.
Εδώ θα ανακτήσουμε τον `κύριο` κλάδο του δεματιού σε έναν κλάδο που ονομάζεται `other-master` στο αποθετήριό μας:

[source,console]

$ git fetch ../commits.bundle master:other-master From ../commits.bundle * [new branch] master → other-master

Τώρα βλέπουμε ότι έχουμε τις εισαγόμενες υποβολές στον κλάδο `other-master` καθώς και τις υποβολές που έχουμε κάνει εν τω μεταξύ στον δικό μας κλάδο `master`.

[source,console]

$ git log --oneline --decorate --graph --all * 8255d41 (HEAD, master) third commit - first repo | * 71b84da (other-master) last commit - second repo | * c99cf5b fourth commit - second repo | * 7011d3d third commit - second repo |/ * 9a466c5 second commit * b1ec324 first commit

Έτσι, η `git bundle` μπορεί να είναι πραγματικά χρήσιμη για να μοιραζόμαστε ή να κάνουμε δικτυακές ενέργειες όταν δεν έχουμε το κατάλληλο δίκτυο ή κοινόχρηστο αποθετήριο για να τις κάνουμε.



[[r_replace]]
=== Replace

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

Η εντολή `replace` μάς επιτρέπει να ορίσουμε ένα αντικείμενο στο Git και να λέμε, ``κάθε φορά που βλέπουμε αυτό, θα υποκριθούμε ότι είναι κάτι άλλο''.
Αυτό είναι συνήθως χρήσιμο για την αντικατάσταση μιας υποβολής στο ιστορικό μας με μία άλλη.

Για παράδειγμα, ας υποθέσουμε ότι έχουμε ένα τεράστιο ιστορικό και θέλουμε να χωρίσουμε το αποθετήριό μας σε ένα σύντομο, πρόσφατο ιστορικό για τους νέους προγραμματιστές και ένα πολύ εκτενέστερο για όσους ενδιαφέρονται για εξόρυξη δεδομένων.
Μπορούμε να μεταμοσχεύσουμε το ένα ιστορικό στο άλλο με την `replace`, αντικαθιστώντας την παλιότερη υποβολή στη νέα γραμμή παραγωγής με την πιο πρόσφατη υποβολή στην παλαιότερη.
Αυτό είναι ωραίο διότι σημαίνει ότι δεν χρειάζεται να ξαναγράψουμε κάθε υποβολή στο νέο ιστορικό, όπως θα έπρεπε κανονικά να κάνουμε για να τα ενώσουμε μαζί (επειδή η γονικότητα επηρεάζει τον αριθμό SHA-1).

Ας το δοκιμάσουμε.
Ας πάρουμε ένα υπάρχον αποθετήριο, το χωρίζουμε σε δύο αποθετήρια, ένα πρόσφατο και ένα ιστορικό και στη συνέχεια θα δούμε πώς μπορούμε να τα ξανασυνδυάσουμε χωρίς να τροποποιήσουμε τις τιμές SHA-1 των νέων αποθετηρίων μέσω `replace`.

Θα χρησιμοποιήσουμε ένα απλό αποθετήριο με πέντε απλές υποβολές:

[source,console]

$ git log --oneline ef989d8 fifth commit c6e1e95 fourth commit 9c68fdc third commit 945704c second commit c1822cf first commit

Θέλουμε να το χωρίσουμε σε δύο ιστορικά.
Το ένα ιστορικό πηγαίνει από την υποβολή ένα στην υποβολή τέσσερα —αυτό θα είναι το ``αρχαίο'' ιστορικό.
Το δεύτερο θα είναι μόνον οι υποβολές τέσσερα και πέντε —αυτό θα είναι το πρόσφατο ιστορικό.

image::images/replace1.png[]

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

[source,console]

$ git branch history c6e1e95 $ git log --oneline --decorate ef989d8 (HEAD, master) fifth commit c6e1e95 (history) fourth commit 9c68fdc third commit 945704c second commit c1822cf first commit

image::images/replace2.png[]

Τώρα μπορούμε να ωθήσουμε τον νέο κλάδο `history` στον κλάδο `master` του νέου αποθετηρίου μας:

[source,console]

$ git remote add project-history https://github.com/schacon/project-history $ git push project-history history:master Counting objects: 12, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (12/12), 907 bytes, done. Total 12 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (12/12), done. To git@github.com:schacon/project-history.git * [new branch] history → master

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

////
[source,console]

$ git log --oneline --decorate ef989d8 (HEAD, master) fifth commit c6e1e95 (history) fourth commit 9c68fdc third commit 945704c second commit c1822cf first commit

////

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

Για να γίνει αυτό, πρέπει να επιλέξουμε ένα σημείο  στο οποίο θα γίνει η διακλάδωση· αυτό για εμάς είναι η τρίτη υποβολή, δηλαδή, η `9c68fdc` στη γλώσσα του SHA.
Έτσι, η υποβολή-βάση θα διακλαδώνεται από αυτό το δέντρο.
Μπορούμε να δημιουργήσουμε την υποβολή μας χρησιμοποιώντας την εντολή `commit-tree`, η οποία παίρνει ακριβώς ένα δέντρο και θα μας επιστρέψει τον αριθμό SHA-1 ενός ολοκαίνουριου ορφανού αντικειμένου υποβολής.

[source,console]

$ echo get history from blah blah blah | git commit-tree 9c68fdc^{tree} 622e88e9cbfbacfb75b5279245b9fb38dfea10cf

[NOTE]
=====
Η εντολή `commit-tree` είναι μία από ένα σύνολο εντολών που συνήθως αναφέρονται ως εντολές ``διοχέτευσης''.
Είναι εντολές που γενικά δεν προορίζονται για άμεση χρήση αλλά χρησιμοποιούνται από **άλλες** εντολές του Git για να κάνουν μικρότερες εργασίες.
Σε ορισμένες περιπτώσεις, όταν κάνουμε πιο περίεργα πράγματα όπως αυτό, μας επιτρέπουν να κάνουμε πράγματα πραγματικά χαμηλού επιπέδου αλλά δεν προορίζονται για καθημερινή χρήση.
Θα δούμε περισσότερα σχετικά με τις εντολές υδραυλικών στην ενότητα <<r_plumbing_porcelain>>.
=====

image::images/replace3.png[]

Τώρα, λοιπόν, που έχουμε μια υποβολή-βάση, μπορούμε να αλλάξουμε τη βάση του υπόλοιπου του ιστορικού μας πάνω σε αυτήν με την `git rebase --onto`.
Το όρισμα `--onto` θα είναι το SHA-1 που μόλις πήραμε από την `commit-tree` και η νέα βάση θα είναι η τρίτη υποβολή (ο γονέας της πρώτης υποβολής που θέλουμε να κρατήσουμε, δηλαδή της `9c68fdc`):

[source,console]

$ git rebase --onto 622e88 9c68fdc First, rewinding head to replay your work on top of it…​ Applying: fourth commit Applying: fifth commit

image::images/replace4.png[]

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

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

[source,console]

$ git clone https://github.com/schacon/project $ cd project

$ git log --oneline master e146b5f fifth commit 81a708d fourth commit 622e88e get history from blah blah blah

$ git remote add project-history https://github.com/schacon/project-history $ git fetch project-history From https://github.com/schacon/project-history * [new branch] master → project-history/master

Τώρα έχει τις πρόσφατες υποβολές του στον κλάδο `master` και τις ``αρχαίες'' υποβολές στον κλάδο `project-history/master`.

[source,console]

$ git log --oneline master e146b5f fifth commit 81a708d fourth commit 622e88e get history from blah blah blah

$ git log --oneline project-history/master c6e1e95 fourth commit 9c68fdc third commit 945704c second commit c1822cf first commit

Για να τις συνδυάσουμε, μπορούμε απλά να καλέσουμε το `git replace` με την υποβολή που θέλουμε να αντικαταστήσουμε και μετά την υποβολή με την οποία θέλουμε να την αντικαταστήσουμε.
Αφού θέλουμε να αντικαταστήσουμε την ``τέταρτη'' υποβολή στον κύριο κλάδο με την ``τέταρτη'' υποβολή στον κλάδο `project-history/master`:

[source,console]

$ git replace 81a708d c6e1e95

Αν κοιτάξουμε τώρα το ιστορικό του κλάδου `master`, φαίνεται να μοιάζει με αυτό:

[source,console]

$ git log --oneline master e146b5f fifth commit 81a708d fourth commit 9c68fdc third commit 945704c second commit c1822cf first commit

Καλή φάση, σωστά; Χωρίς να χρειάζεται να αλλάξουμε όλα τα SHA-1s upstream, μπορέσαμε να αντικαταστήσουμε μια υποβολή στο ιστορικό μας με μια εντελώς διαφορετική υποβολή και όλα τα συνήθη εργαλεία (`bisect`, `blame` κ.λπ.) θα δουλεύουν με τον τρόπο με τον οποίο θα αναμέναμε.

image::images/replace5.png[]

Κάτι ενδιαφέρον είναι ότι εξακολουθεί να εμφανίζεται η `81a708d` ως SHA-1, παρόλο που στην πραγματικότητα χρησιμοποιούνται τα δεδομένα της `c6e1e95` με την οποία την αντικαταστήσαμε.
Ακόμα και αν εκτελέσουμε μια εντολή όπως η `cat-file`, θα μας δείξει τα αντικατασταθέντα δεδομένα:

[source,console]

$ git cat-file -p 81a708d tree 7bc544cf438903b65ca9104a1e30345eee6c083d parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252 author Scott Chacon <schacon@gmail.com> 1268712581 -0700 committer Scott Chacon <schacon@gmail.com> 1268712581 -0700

fourth commit

Ας θυμηθούμε ότι ο πραγματικός γονέας της `81a708d` ήταν η προσωρινή υποβολή `622e88e` και  όχι η `9c68fdce` όπως δηλώνεται εδώ.

Κάτι ακόμα ενδιαφέρον είναι ότι αυτά τα δεδομένα διατηρούνται στις αναφορές μας:

[source,console]

$ git for-each-ref e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/heads/master c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/remotes/history/master e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/HEAD e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/master c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/replace/81a708dd0e167a3f691541c7a6463343bc457040

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


[[r_credential_caching]]
=== Αποθήκευση διαπιστευτηρίων

(((διαπιστευτήρια)))
(((εντολές git, διαπιστευτήρια)))
Εάν χρησιμοποιούμε τη μεταφορά μέσω SSH για τη σύνδεση με απομακρυσμένα αποθετήρια, είναι πιθανό να έχουμε ένα κλειδί χωρίς κωδική φράση, η οποία μας επιτρέπει να μεταφέρουμε δεδομένα με ασφάλεια χωρίς να πληκτρολογούμε το όνομα χρήστη και τον κωδικό πρόσβασής μας.
Ωστόσο, αυτό δεν είναι δυνατό με τα πρωτόκολλα HTTP —κάθε σύνδεση χρειάζεται ένα όνομα χρήστη και έναν κωδικό πρόσβασης.
Αυτό γίνεται ακόμα πιο δύσκολο για συστήματα με ταυτοποίηση δύο παραγόντων, όπου το διακριτικό (token) που χρησιμοποιούμε για έναν κωδικό πρόσβασης παράγεται τυχαία και δεν μπορεί να ωθηθεί.

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

* Η προεπιλογή είναι να μην αποθηκεύει καθόλου διαπιστευτήρια.
  Κάθε σύνδεση θα μας ζητάει το όνομα χρήστη και τον κωδικό πρόσβασής μας.
* Κατά τη λειτουργία `cache` διατηρεί τα διαπιστευτήρια στη μνήμη για ορισμένο χρονικό διάστημα.
  Κανένας από τους κωδικούς πρόσβασης δεν αποθηκεύεται ποτέ στον δίσκο και οι κωδικοί πρόσβασης διαγράφονται από την προσωρινή μνήμη μετά από 15 λεπτά.
* Κατά τη λειτουργία `store` τα διαπιστευτήρια αποθηκεύονται σε ένα αρχείο απλού κειμένου στον δίσκο και δεν εκπνέουν ποτέ.
  Αυτό σημαίνει ότι μέχρι να αλλάξουμε τον κωδικό πρόσβασης για τον κεντρικό υπολογιστή Git, δεν θα χρειαστεί ποτέ να πληκτρολογήσουμε ξανά τα διαπιστευτήριά μας.
  Το μειονέκτημα αυτής της προσέγγισης είναι ότι οι κωδικοί πρόσβασης αποθηκεύονται σε αρχείο απλού κειμένου στον προσωπικό μας κατάλογο.
* Εάν χρησιμοποιούμε Mac, το Git έρχεται με τη λειτουργία `osxkeychain`, η οποία αποθηκεύει τα διαπιστευτήρια στην ασφαλή κλειδοθήκη (keychain) που είναι συνδεδεμένη με το λογαριασμό μας στο σύστημα.
  Αυτή η μέθοδος αποθηκεύει τα διαπιστευτήριά μας στον δίσκο και δεν λήγει ποτέ, αλλά είναι κρυπτογραφημένα με το ίδιο σύστημα που αποθηκεύει τα πιστοποιητικά HTTPS και την αυτόματη συμπλήρωση του Safari.
* Εάν χρησιμοποιούμε Windows, μπορούμε να εγκαταστήσουμε έναν βοηθό που ονομάζεται `winstore`.
  Είναι παρόμοιος με τον βοηθό `osxkeychain` που περιγράφεται παραπάνω, αλλά χρησιμοποιεί το Windows Credential Store (Χώρος Αποθήκευσης Διαπιστευτηρίων) για τον έλεγχο ευαίσθητων πληροφοριών.
  Διατίθεται στη διεύθυνση https://gitcredentialstore.codeplex.com[].

Μπορούμε να επιλέξουμε μία από αυτές τις μεθόδους ρυθμίζοντας την τιμή μίας μεταβλητής διαμόρφωσης του Git:

[source,console]

$ git config --global credential.helper cache

Μερικοί από αυτούς τους βοηθούς έχουν επιλογές.
Ο βοηθός `store` μπορεί να πάρει ένα όρισμα `--file <διαδρομή>`, το οποίο καθορίζει πού θα αποθηκεύεται το αρχείο απλού κειμένου (η προεπιλογή είναι το `~/.git-credentials`).
Ο βοηθός `cache` δέχεται την επιλογή `--timeout <δευτερόλεπτα> ', η οποία αλλάζει το χρονικό διάστημα για το οποίο ο δαίμονάς του συνεχίζει να τρέχει (η προεπιλογή είναι `900`, δηλαδή 15 λεπτά).
Ακολουθεί ένα παράδειγμα του πώς θα ρυθμίζουμε τον βοηθό `store` με ένα όνομα προσαρμοσμένου αρχείου:

[source,console]

$ git config --global credential.helper store --file ~/.my-credentials

Το Git ακόμη μας επιτρέπει να ρυθμίσουμε διάφορους βοηθούςς.
Όταν αναζητάμε διαπιστευτήρια για έναν συγκεκριμένο κεντρικό υπολογιστή, το Git θα τα εξετάζει με τη σειρά και θα σταματήσει μετά την πρώτη απάντηση.
Κατά την αποθήκευση διαπιστευτηρίων, το Git θα στείλει το όνομα χρήστη και τον κωδικό πρόσβασης σε *όλους* τους βοηθούς της λίστας και αυτοί μπορούν να επιλέξουν τι να τους κάνουν.
Ακολουθεί ένα παράδειγμα του πώς θα έμοιαζε το `.gitconfig` αν είχαμε ένα αρχείο διαπιστευτηρίων σε ένα USB stick, αλλά θέλαμε να χρησιμοποιήσουμε την προσωρινή μνήμη για να γλιτώσουμε λίγη πληκτρολόγηση εάν η μονάδα δεν είναι συνδεδεμένη:

[source,ini]
helper = store --file /mnt/thumbdrive/.git-credentials
helper = cache --timeout 30000
==== Πώς λειτουργεί

Πώς λειτουργεί όλο αυτό;
Η βασική εντολή του Git για το σύστημα βοηθών διαπιστευτηρίων είναι η `git credential`, η οποία παίρνει μια εντολή ως όρισμα και άλλες εισόδους από το stdin.

Αυτό είναι ίσως ευκολότερο να κατανοηθεί με ένα παράδειγμα.
Ας υποθέσουμε ότι ένας βοηθός διαπιστευτηρίων έχει ρυθμιστεί και ο βοηθός έχει αποθηκεύσει τα διαπιστευτήρια για τον διακομιστή `mygithost`.
Ακολουθεί μια συνεδρία που χρησιμοποιεί την εντολή `fill`, η οποία ενεργοποιείται όταν το Git προσπαθεί να βρει διαπιστευτήρια για έναν διακομιστή:

[source,console]

$ git credential fill <1> protocol=https <2> host=mygithost <3> protocol=https <4> host=mygithost username=bob password=s3cre7 $ git credential fill <5> protocol=https host=unknownhost

Username for https://unknownhost: bob Password for https://bob@unknownhost: protocol=https host=unknownhost username=bob password=s3cre7

<1> Αυτή είναι η γραμμή εντολών που ενεργοποιεί την αλληλεπίδραση.
<2> Η `git credential` στη συνέχεια αναμένει είσοδο από το stdin.
    Το παρέχουμε με αυτά που γνωρίζουμε: το πρωτόκολλο και το όνομα του κεντρικού υπολογιστή.
<3> Μια κενή γραμμή υποδεικνύει ότι η είσοδος είναι πλήρης και το σύστημα διαπιστευτηρίων πρέπει να απαντήσει με αυτό που γνωρίζει.
<4> Η `git credential` γράφει στο stdout τις πληροφορίες που βρήκε.
<5> Εάν δεν εντοπιστούν διαπιστευτήρια, το Git ζητάει από τον χρήστη το όνομα χρήστη και τον κωδικό πρόσβασης και τα επαναφέρει στην κλήση του stdout (εδώ είναι συνδεδεμένα στην ίδια κονσόλα).

Το σύστημα διαπιστευτηρίων στην πραγματικότητα καλεί ένα πρόγραμμα που είναι ξεχωριστό από το ίδιο το Git· ποιο πρόγραμμα και πώς το καλεί εξαρτάται από την τιμή της μεταβλητής ρύθμισης `credential.helper`.
Μπορεί να πάρει διάφορες μορφές:

[options="header"]
|======
| Configuration Value | Behavior
| `foo` | Runs `git-credential-foo`
| `foo -a --opt=bcd` | Runs `git-credential-foo -a --opt=bcd`
| `/absolute/path/foo -xyz` | Runs `/absolute/path/foo -xyz`
| `!f() { echo "password=s3cre7"; }; f` | Code after `!` evaluated in shell
|======

Έτσι οι βοηθοί που περιγράφηκαν παραπάνω στην πραγματικότητα ονομάζονται `git-credential-cache`, `git-credential-store` κ.ο.κ. και μπορούμε να τους ρυθμίσουμε ώστε να λάβουν ορίσματα από τη γραμμή εντολών.
Η γενική μορφή είναι `git-credential-foo [args] <ενέργεια>`.
Το πρωτόκολλο stdin/stdout είναι το ίδιο με αυτό της `git-credential`, αλλά χρησιμοποιούν ένα ελαφρώς διαφορετικό σύνολο ενεργειών:

* `get` είναι ένα αίτημα για ένα ζεύγος ονόματος χρήστη/κωδικού πρόσβασης.
* `store` είναι ένα αίτημα για να αποθηκεύσουμε ένα σύνολο διαπιστευτηρίων στη μνήμη αυτού του βοηθού.
* `erase` είναι αίτημα διαγραφής των διαπιστευτηρίων για τις δοσμένες ιδιότητες από τη μνήμη αυτού του βοηθού.

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

Ακολουθεί το ίδιο παράδειγμα όπως προηγουμένως, αλλά παρακάμπτοντας την `git-credential` και πηγαίνοντας κατευθείαν στην `git-credential-store`:

[source,console]

$ git credential-store --file ~/git.store store <1> protocol=https host=mygithost username=bob password=s3cre7 $ git credential-store --file ~/git.store get <2> protocol=https host=mygithost

username=bob <3> password=s3cre7

<1> Εδώ λέμε στην `git-credential-store` να αποθηκεύσει κάποια διαπιστευτήρια: το όνομα χρήστη `bob` και ο κωδικός πρόσβασης `s3cre7` πρέπει να χρησιμοποιούνται όταν προσπελασζουμε τη διεύθυνση `https://mygithost`.
<2> Τώρα ανακτούμε αυτά τα διαπιστευτήρια.
    Παρέχουμε τα τμήματα της σύνδεσης που ήδη γνωρίζουμε (`https://mygithost`) και μια κενή γραμμή.
<3> Η `git-credential-store` απαντά με το όνομα χρήστη και τον κωδικό πρόσβασης που αποθηκεύσαμε παραπάνω.

Το αρχείο `~/git.store` τώρα μοιάζει με αυτό:

[source]
Είναι μόνο για μια σειρά γραμμών, καθεμία από τις οποίες περιέχει μια διεύθυνση URL διακοσμημένη με διαπιστευτήρια.
Οι βοηθοί `osxkeychain` και `winstore` χρησιμοποιούν την εγγενή μορφή των καταστημάτων υποστήριξης, ενώ η `cache` χρησιμοποιεί τη δική της μορφή μνήμης (η οποία δεν μπορεί να διαβάσει καμία άλλη διαδικασία).

==== Μία εξατομικευμένη προσωρινή μνήμη διαπιστευτηρίων

Δεδομένου ότι η `git-credential-store` και οι φίλοι της είναι προγράμματα ξεχωριστά από το Git, δεν είναι και μεγάλο διανοητικό άλμα να συνειδητοποιήσουμε ότι _οποιοδήποτε_ πρόγραμμα μπορεί να είναι βοηθός διαπιστευτηρίων Git.
Οι βοηθοί που παρέχονται από το Git καλύπτουν πολλές κοινές περιπτώσεις χρήσης αλλά όχι όλες.
Για παράδειγμα, ας υποθέσουμε ότι η ομάδα μας έχει ορισμένα διαπιστευτήρια που είναι κοινά σε ολόκληρη την ομάδα, ίσως για ανάπτυξη.
Αυτά αποθηκεύονται σε έναν κοινόχρηστο κατάλογο, αλλά δεν θέλουμε να τα αντιγράψουμε στο δικό μας κατάστημα διαπιστευτηρίων, επειδή αλλάζουν συχνά.
Κανένας από τους υπάρχοντες βοηθούς δεν καλύπτει αυτήν την περίπτωση· ας δούμε τι θα χρειαζόταν για να γράψουμε το δικό μας.
Υπάρχουν πολλά χαρακτηριστικά-κλειδά που πρέπει να έχει ένα τέτοιο πρόγραμμα:

. Η μόνη ενέργεια την οποία πρέπει να προσέξουμε πολύ είναι η `get`· Οι `store` και `erase` είναι λειτουργίες εγγραφής, επομένως απλά θα τερματίσουμε χωρίς έξοδο όταν τις λάβουμε.
. Η μορφή αρχείου του αρχείου κοινόχρηστων διαπιστευτηρίων είναι ίδια με αυτήν που χρησιμοποιείται από την `git-credential-store`.
. Η τοποθεσία αυτού του αρχείου είναι αρκετά τυπική, αλλά θα πρέπει να δώσουμε τη δυνατότητα στον χρήστη να δίνει άλλη διαδρομή στην περίπτωση που το θέλει.

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

[source,ruby]
--------
#!/usr/bin/env ruby

require 'optparse'

path = File.expand_path '~/.git-credentials' # (1)
OptionParser.new do |opts|
    opts.banner = 'USAGE: git-credential-read-only [options] <action>'
    opts.on('-f', '--file PATH', 'Specify path for backing store') do |argpath|
        path = File.expand_path argpath
    end
end.parse!

exit(0) unless ARGV[0].downcase == 'get' # (2)
exit(0) unless File.exists? path

known = {} # (3)
while line = STDIN.gets
    break if line.strip == ''
    k,v = line.strip.split '=', 2
    known[k] = v
end

File.readlines(path).each do |fileline| # (4)
    prot,user,pass,host = fileline.scan(/^(.*?):\/\/(.*?):(.*?)@(.*)$/).first
    if prot == known['protocol'] and host == known['host'] then
        puts "protocol=#{prot}"
        puts "host=#{host}"
        puts "username=#{user}"
        puts "password=#{pass}"
        exit(0)
    end
end

--------

<1> Εδώ αναλύουμε τις επιλογές της γραμμής εντολών, επιτρέποντας στον χρήστη να καθορίσει το αρχείο εισόδου. Η προεπιλογή είναι `~/.git-credentials`.
<2> Αυτό το πρόγραμμα αποκρίνεται μόνον εάν η ενέργεια είναι `get` και το αρχείο backing-store υπάρχει.
<3> Αυτός ο βρόχος διαβάζει από την stdin μέχρι να συναντήσει την πρώτη κενή γραμμή.
    Οι είσοδοι αποθηκεύονται στο πίνακα αναζήτηση `known` για μεταγενέστερη αναφορά.
<4> Αυτός ο βρόχος διαβάζει τα περιεχόμενα του αρχείου αποθήκευσης αναζητώντας αντιστοιχίες.
    Εάν το πρωτόκολλο και ο κεντρικός υπολογιστής από τη μεταβλητή  `known` ταιριάζουν με αυτήν τη γραμμή, το πρόγραμμα εκτυπώνει τα αποτελέσματα στην stdout και τερματίζει.

Αποθηκεύουμε τον βοηθό μας ως `git-credential-read-only`, τον βάζουμε κάπου στο `PATH` μας και τον επισημόνουμε ως εκτελέσιμο.
Ακολουθεί μια διαδραστική συνεδρία:

[source,console]

$ git credential-read-only --file=/mnt/shared/creds get protocol=https host=mygithost

protocol=https host=mygithost username=bob password=s3cre7

Επειδή το όνομά του ξεκινάει με `git-`, μπορούμε να χρησιμοποιήσουμε την απλή σύνταξη για την τιμή της μεταβλητής ρύθμισης:

[source,console]

$ git config --global credential.helper read-only --file /mnt/shared/creds

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



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

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


[#ch08-customizing-git]
[[r_customizing_git]]
== Εξατομίκευση του Git

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

[[r_git_config]]
=== Διαμόρφωση Git

(((εντολές git, config)))
Όπως έχουμε δει σύντομα στο <<ch01-introduction>>, μπορούμε να καθορίσουμε τις ρυθμίσεις των παραμέτρων του Git με την εντολή `git config`.
Ένα από τα πρώτα πράγματα που κάναμε ήταν να δημιουργήσουμε το όνομά μας και τη διεύθυνση ηλεκτρονικού ταχυδρομείου μας:

[source,console]

$ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com

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

Πρώτα όμως, μια γρήγορη ανασκόπηση: Το Git χρησιμοποιεί μια σειρά αρχείων ρυθμίσεων (configuration files) για να καθορίσει τη μη προεπιλεγμένη συμπεριφορά που μπορεί να θέλουμε.
Το πρώτο μέρος στο οποίο ψάχνει το Git για αυτές τις τιμές είναι σε ένα αρχείο `/etc/gitconfig`, το οποίο περιέχει τιμές για κάθε χρήστη στο σύστημα και σε όλα τα αποθετήριά του.
Εάν τρέξουμε το `git config` με την επιλογή `--system`, τότε το Git διαβάζει από και γράφει σε αυτό το αρχείο συγκεκριμένα.

Το επόμενο μέρος στο οποίο ψάχνει το Git είναι το αρχείο `~/.gitconfig` (ή `~/.config/git/ config`), το οποίο είναι συγκεκριμένος για κάθε χρήστη.
Μπορούμε να κάνουμε το Git να διαβάζει από και να γράφει σε αυτό το αρχείο περνώντας την επιλογή `--global`.

Τέλος, το Git αναζητά τιμές διαμόρφωσης στο αρχείο ρυθμίσεων στον κατάλογο του Git (`.git/config`) του οποιουδήποτε αποθετηρίου χρησιμοποιούμε αυτήν τη στιγμή.
Αυτές οι τιμές είναι συγκεκριμένες για αυτό το αποθετήριο.

Καθένα από αυτά τα ``επίπεδα'' (σύστημα, παγκόσμιο, τοπικό) παρακάμπτει τις τιμές του προηγούμενου επίπεδου, έτσι ώστε, για παράδειγμα, οι τιμές στο `.git/config` παρακάμπτουν εκείνες του `/etc/gitconfig`.

[NOTE]
====
Τα αρχεία ρυθμίσεων του Git είναι απλό κείμένο, επομένως μπορούμε επίσης να ορίσουμε αυτές τις τιμές με χειροκίνητη επεξεργασία του αρχείου και εισαγωγή της σωστής σύνταξης.
Πάντως γενικά είναι ευκολότερο να εκτελέσουμε την εντολή `git config`.
====

==== Βασική διαμόρφωση πελάτη

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

[source,console]

$ man git-config

Αυτή η εντολή παραθέτει περιέχει όλες τις διαθέσιμες επιλογές με αρκετές λεπτομέρειες.
Μπορούμε επίσης να βρούμε αυτό το υλικό αναφοράς στη διεύθυνση http://git-scm.com/docs/git-config.html[].

((($EDITOR)))((($VISUAL, βλ. $EDITOR)))
===== 'core.editor`

Προκειμένου να δημιουργήσουμε και να επεξεργαστούμε τα μηνύματα `commit` και `tag`, το Git χρησιμοποιεί όποιον επεξεργαστή κειμένου έχουμε ορίσει ως προεπιλεγμένο (`$VISUAL` ή `$EDITOR`) ή αλλιώς μετακυλά στον επεξεργαστή `vi` .
Για να αλλάξουμε αυτήν την προεπιλογή σε κάτι άλλο, μπορούμε να χρησιμοποιήσουμε τη ρύθμιση `core.editor`:

[source,console]

$ git config --global core.editor emacs

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

===== `commit.template`

(((commit templates)))
Εάν ορίσουμε αυτήν την παράμετρο να έχει ως τιμή τη διαδρομή ενός αρχείου στο σύστημά μας, το Git θα χρησιμοποιεί αυτό το αρχείο ως το προεπιλεγμένο μήνυμα όταν υποβάλλουμε.
Για παράδειγμα, ας υποθέσουμε ότι δημιουργούμε ένα πρότυπο (template) αρχείο στο `~/.gitmessage.txt` που μοιάζει με αυτό:

[source]

subject line

what happened

Για να πούμε στο Git να το χρησιμοποιήσει ως προεπιλεγμένο μήνυμα που εμφανίζεται στον επεξεργαστή μας όταν εκτελούμε το `git commit`, ορίζουμε την παράμετρο διαμόρφωσης `commit.template`:

[source,console]

$ git config --global commit.template ~/.gitmessage.txt $ git commit

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

[source]

subject line

what happened

scroll-to-top