Git
Chapters ▾ 2nd Edition

3.5 Διακλαδώσεις στο Git - Απομακρυσμένοι κλάδοι

Απομακρυσμένοι κλάδοι

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

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

Έχουν τη μορφή <απομακρυσμένο_αποθετήριο>/<κλάδος>. Για παράδειγμα, αν θέλουμε να δούμε σε ποια κατάσταση ήταν ο κλάδος 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.

Τοπικά και απομακρυσμένα αποθετήρια μετά την κλωνοποίηση.
Figure 30. Τοπικά και απομακρυσμένα αποθετήρια μετά την κλωνοποίηση.

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

Η τοπική και απομακρυσμένη δουλειά αποκλίνουν.
Figure 31. Η τοπική και απομακρυσμένη δουλειά αποκλίνουν.

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

Η εντολή `git fetch` ενημερώνει τις απομακρυσμένες αναφορές.
Figure 32. Η εντολή git fetch ενημερώνει τις απομακρυσμένες αναφορές.

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

Προσθήκη ενός επιπλέον απομακρυσμένου διακομιστή.
Figure 33. Προσθήκη ενός επιπλέον απομακρυσμένου διακομιστή.

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

Κλάδος τηλεπαρακολούθησης για τον κλάδο `teamone/master`.
Figure 34. Κλάδος τηλεπαρακολούθησης για τον κλάδο teamone/master.

Ωθήσεις

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

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

$ 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/ στην ενότητα Εσωτερική λειτουργία του Git, αλλά προς το παρόν μπορούμε να το αγνοήσουμε. Επίσης μπορούμε να τρέξουμε την εντολή git push origin serverfix:serverfix, που κάνει ακριβώς το ίδιο πράγμα —λέει, “Πάρε το δικό μου serverfix και κάντο το serverfix του απομακρυσμένου διακομιστή”. Αυτήν τη μορφή της εντολής τη χρησιμοποιούμε για να ωθήσουμε έναν τοπικό κλάδο σε έναν απομακρυσμένο που έχει διαφορετικό όνομα. Αν δεν θέλαμε να ονομάζεται serverfix στο απομακρυσμένο αποθετήριο, τότε μπορούμε να τρέξουμε την εντολή git push origin serverfix:awesomebranch ώστε να ωθήσουμε τον τοπικό μας κλάδο serverfix στον κλάδο με όνομα awesomebranch στο απομακρυσμένο αποθετήριο.

Note
Δεν χρειάζεται να γράφουμε τον κωδικό μας κάθε φορά.

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

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

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

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

$ 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 στον οποίο μπορούμε να εργαστούμε, μπορούμε να τον αποκολλήσουμε από τον κλάδο τηλεπαρακολούθησης:

$ 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.

Παρακολούθηση κλάδων

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

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

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

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

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

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

$ 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.

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

$ 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, αν θέλουμε.

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

$ 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) κατά δύο, δηλαδή ότι έχουμε κάνει δύο υποβολές τοπικά που δεν έχουν ωθηθεί στον διακομιστή. Επίσης μπορούμε να δούμε ότι ο τοπικός μας κλάδος master παρακολουθεί τον origin/master και είναι ενημερωμένος. Στη συνέχεια βλέπουμε ότι ο κλάδος μας serverfix παρακολουθεί τον κλάδο server-fix-good στον διακομιστή teamone και προηγείται κατά τρία και υστερεί κατά ένα, που σημαίνει ότι υπάρχει μία υποβολή στον διακομιστή που δεν την έχουμε συγχωνεύσει ακόμη και τρεις τοπικές υποβολές που δεν έχουμε ωθήσει ακόμη. Τέλος, βλέπουμε ότι ο κλάδος μας testing δεν παρακολουθεί κανέναν απομακρυσμένο κλάδο.

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

$ git fetch --all; git branch -vv

Έλξη

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

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

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

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

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

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