-
1. Ξεκινώντας με το Git
-
2. Τα θεμελιώδη στοιχεία του Git
-
3. Διακλαδώσεις στο Git
-
4. Το Git στον διακομιστή
- 4.1 Τα πρωτόκολλα
- 4.2 Εγκατάσταση του Git σε διακομιστή
- 4.3 Δημιουργία δημόσιου κλειδιού SSH
- 4.4 Στήσιμο του διακομιστή
- 4.5 Δαίμονες του Git
- 4.6 Έξυπνο HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Επιλογές φιλοξενίας από τρίτους
- 4.10 Ανακεφαλαίωση
-
5. Κατανεμημένο Git
-
6. GitHub
-
7. Εργαλεία του Git
- 7.1 Επιλογή αναθεώρησης
- 7.2 Διαδραστική εργασία με το στάδιο καταχώρισης
- 7.3 Αποθέματα και Καθαρισμός
- 7.4 Υπογραφή της δουλειάς μας
- 7.5 Αναζήτηση
- 7.6 Η ιστορία ξαναγράφεται
- 7.7 Απομυθοποίηση της reset
- 7.8 Προχωρημένη Συγχώνευση
- 7.9 Rerere
- 7.10 Αποσφαλμάτωση με το Git
- 7.11 Υπομονάδες
- 7.12 Δεμάτιασμα δεδομένων
- 7.13 Replace
- 7.14 Αποθήκευση διαπιστευτηρίων
- 7.15 Ανακεφαλαίωση
-
8. Εξατομίκευση του Git
-
9. Το Git και άλλα συστήματα
- 9.1 Το Git ως πελάτης
- 9.2 Μετανάστευση στο Git
- 9.3 Ανακεφαλαίωση
-
10. Εσωτερική λειτουργία του Git
- 10.1 Διοχετεύσεις και πορσελάνες
- 10.2 Αντικείμενα του Git
- 10.3 Αναφορές του Git
- 10.4 Πακετάρισμα αρχείων
- 10.5 Τα refspec
- 10.6 Πρωτόκολλα μεταφοράς
- 10.7 Διατήρηση και ανάκτηση δεδομένων
- 10.8 Μεταβλητές περιβάλλοντος
- 10.9 Ανακεφαλαίωση
-
A1. Appendix A: Το Git σε άλλα περιβάλλοντα
- A1.1 Γραφικές διεπαφές
- A1.2 Το Git στο Visual Studio
- A1.3 Git στο Visual Studio Code
- A1.4 Git στο IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git στο Sublime Text
- A1.6 Το Git στο Bash
- A1.7 Το Git στο Zsh
- A1.8 Το Git στο Powershell
- A1.9 Ανακεφαλαίωση
-
A2. Appendix B: Ενσωμάτωση του Git στις εφαρμογές μας
- A2.1 Γραμμή εντολών Git
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Appendix C: Εντολές Git
- A3.1 Ρύθμιση και διαμόρφωση
- A3.2 Λήψη και δημιουργία έργων
- A3.3 Βασική λήψη στιγμιοτύπων
- A3.4 Διακλάδωση και συγχώνευση
- A3.5 Κοινή χρήση και ενημέρωση έργων
- A3.6 Επιθεώρηση και σύγκριση
- A3.7 Αποσφαλμάτωση
- A3.8 Επιθέματα
- A3.9 Ηλεκτρονικό ταχυδρομείο
- A3.10 Εξωτερικά Συστήματα
- A3.11 Διοίκηση
- A3.12 Εντολές διοχέτευσης
3.6 Διακλαδώσεις στο Git - Αλλαγή βάσης (rebasing)
Αλλαγή βάσης (rebasing)
Στο Git, υπάρχουν δύο βασικοί τρόποι ενσωμάτωσης αλλαγών από έναν κλάδο σε έναν άλλο: με την εντολή merge και την εντολή rebase (αλλαγή βάσης).
Σε αυτή την ενότητα θα μάθουμε τι είναι η αλλαγή βάσης, πώς την κάνουμε, γιατί θεωρείται εκπληκτικό εργαλείο και σε ποιες περιπτώσεις δεν πρέπει να τη χρησιμοποιούμε.
Η βασική μορφή αλλαγής βάσης
Εάν επιστρέψουμε σε ένα παλαιότερο παράδειγμα από την ενότητα Συγχωνεύσεις — τα βασικά, θα δούμε ότι η δουλειά μας είχε αποκλίνει και είχαμε κάνει υποβολές σε δύο διαφορετικούς κλάδους.
Ο ευκολότερος τρόπος ενσωμάτωσης των κλάδων, όπως έχουμε ήδη δει, είναι η εντολή merge (συγχώνευση).
Επιτελεί μια τριμερή συγχώνευση μεταξύ των δύο τελευταίων στιγμιότυπων διακλάδωσης (C3 και`C4`) και του πιο πρόσφατου κοινού προγόνου τους (C2), δημιουργώντας ένα νέο στιγμιότυπο (και μία νέα υποβολή).
Ωστόσο, υπάρχει κι ένας άλλος τρόπος: μπορούμε να πάρουμε μόνον το επίθεμα (patch) με τις τροποποιήσεις που εισήχθησαν με την υποβολή C4 και να το εφαρμόσουμε ξανά στο στιγμιότυπο C3.
Στο Git, αυτό ονομάζεται αλλαγή βάσης ή επανατοποθέτηση (rebasing).
Με την εντολή rebase παίρνουμε όλες τις αλλαγές που υποβλήθηκαν σε ένα κλάδο και να τις επαναλαμβάνουμε σε έναν άλλο.
Για αυτό το παράδειγμα, πρώτα θα κάνουμε checkout τον κλάδο experiment και στη συνέχεια θα τον επανατοποθετούσαμε στον κλάδο master (δηλαδή, θα αλλάζαμε τη βάση του από την υποβολή C2 στην υποβολή που δείχνει ο κλάδος master) ως εξής:
$ 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
Σε αυτό το σημείο, μπορούμε να επιστρέψουμε στον κλάδο master και να κάνουμε μία συγχώνευση με ταχυπροώθηση.
$ git checkout master
$ git merge experiment
master
Πλέον το στιγμιότυπο στο οποίο δείχνει η υποβολή C4' είναι ακριβώς ίδιο με αυτό στο οποίο δείχνει η C5 στο παλιότερο παράδειγμα με τη συγχώνευση.
Το τελικό προϊόν της ενσωμάτωσης των αλλαγών είναι ακριβώς το ίδιο, αλλά η αλλαγή της βάσης κρατά το ιστορικό πιο καθαρό.
Αν εξετάσουμε το log ενός επανατοποθετημένου (σε νέα βάση) κλάδου, φαίνεται σαν το ιστορικό να είναι γραμμικό, δηλαδή σαν όλη η εργασία να συνέβη ακολουθιακά παρά το ότι αρχικά γινόταν παράλληλα.
Συχνά θα κάνουμε κάτι τέτοιο για να βεβαιωθούμε ότι οι υποβολές μας εφαρμόζονται χωρίς συγκρούσεις σε έναν απομακρυσμένο κλάδο — ενδεχομένως σε ένα έργο στο οποίο συμβάλλουμε, αλλά δεν το διαχειριζόμαστε.
Σε μία τέτοια περίπτωση, θα κάνουμε τη δουλειά μας σε έναν κλάδο και στη συνέχεια θα αλλάζαμε τη βάση (επανατοποθετούσαμε) της εργασίας μας στον κλάδο origin/master όταν ήμασταν έτοιμοι να υποβάλλουμε τα επιθέματά μας στο κύριο έργο.
Με αυτό τον τρόπο, ο διαχειριστής του έργου δεν χρειάζεται να κάνει καμία εργασία ενσωμάτωσης — απλά μια ταχυπροώθηση ή μια καθαρή εφαρμογή.
Σημειώνουμε ότι το στιγμιότυπο στο οποίο δείχνει η υποβολή στην οποία καταλήγουμε, είναι το ίδιο είτε πρόκειται για την τελευταία από τις επανατοποθετημένες υποβολές μετά από αλλαγή βάσης είτε για την τελική υποβολή συγχώνευσης μετά από μία συγχώνευση — το μόνο που είναι διαφορετικό είναι το ιστορικό. Η αλλαγή βάσης εφαρμόζει τις τροποποιήσεις μίας γραμμής εργασίας σε μία άλλη με τη σειρά που έγιναν, ενώ η συγχώνευση παίρνει τα τελικά στιγμιότυπα και τα συγχωνεύει.
Μερικές ενδιαφέρουσες αλλαγές βάσης
Μπορούμε επίσης να επανατοποθετήσουμε έναν κλάδο πάνω σε κάποιον κλάδο διαφορετικό από αυτόν στον οποίο βασιζόταν αρχικά.
Για παράδειγμα, ας πάρουμε ένα ιστορικό όπως αυτό στο Ιστορικό με έναν θεματικό κλάδο που βασίζεται σε έναν άλλο θεματικό κλάδο.
Έχουμε δημιουργήσει έναν θεματικό κλάδο (server) για να προσθέσουμε κάποια λειτουργικότητα από την πλευρά του διακομιστή στο έργο μας και πραγματοποιήσαμε μια υποβολή.
Στη συνέχεια, δημιουργήσαμε μία ακόμα διακλάδωση για να κάνουμε τις αλλαγές από την πλευρά του πελάτη (client) και κάνουμε επίσης μερικές υποβολές.
Τέλος, επιστρέψαμε στον κλάδο server και κάνουμε μερικές ακόμη υποβολές.
Ας υποθέσουμε ότι αποφασίζουμε να συγχωνεύσουμε τις αλλαγές από την πλευρά του πελάτη στην κεντρική γραμμή που θα δημοσιευτεί, αλλά θέλουμε να αναβάλλουμε τις αλλαγές από την πλευρά του διακομιστή μέχρι να εξεταστούν περαιτέρω.
Μπορούμε να πάρουμε τις αλλαγές από τον κλάδο client που δεν βρίσκονται στον κλάδο server (C8 και`C9`) και να τις αναπαράγουμε στον κύριο κλάδο μας χρησιμοποιώντας την επιλογή --onto της git rebase:
$ git rebase --onto master server client
Αυτή η εντολή ουσιαστικά λέει, “Πήγαινε στον κλάδο client, εντόπισε τα επιθέματα από τότε που απέκλινε από τον κλάδο server και εφάρμοσέ τα στον κλάδο client σαν ο κλάδος client να ήταν κλάδος που απέκλινε από τον κλάδο master”.
Είναι λίγο περίπλοκο, αλλά το αποτέλεσμα είναι μια ομορφιά.
Τώρα μπορούμε να ταχυπροωθήσουμε τον κλάδο master (βλ. Ταχυπροώθηση του κλάδου master ώστε να συμπεριλάβει τις αλλαγές του κλάδου client):
$ git checkout master
$ git merge client
master ώστε να συμπεριλάβει τις αλλαγές του κλάδου client
Ας πούμε ότι τώρα αποφασίζουμε να ενσωματώσουμε και τις αλλάγες του κλάδου server.
Μπορούμε να αλλάξουμε τη βάση του κλάδου server (στον κλάδο master), χωρίς να έχουμε προηγουμένως μεταβεί σε αυτόν, εκτελώντας την εντολή git rebase <basebranch> <topicbranch> — η οποία μας μεταφέρει στον θεματικό κλάδο (σε αυτή την περίπτωση, τον server) και εφαρμόζει τις αλλαγές του στη νέα βάση (master) συγχρόνως:
$ git rebase master server
Το αποτέλεσμα της παραπάνω εντολής φαίνεται στην Αλλαγή της βάσης του κλάδου server στον κλάδο master.
server στον κλάδο master
Στη συνέχεια μπορούμε να ταχυπροωθήσουμε τον κλάδο-βάση (master):
$ git checkout master
$ git merge server
Μπορούμε να αφαιρέσουμε τους κλάδους client και server επειδή όλη δουλειά μας έχει ενσωματωθεί και δεν τους χρειάζόμαστε πια, κάνοντας το ιστορικό μας, μετά από όλη αυτή τη διαδικασία, να μοιάζει με το Τελικό ιστορικό υποβολών:
$ git branch -d client
$ git branch -d server
Οι κίνδυνοι της αλλαγής βάσης
Όμως η ευδαιμονία που μας προσφέρει η αλλαγή βάσης έχει κάποιο αντίτιμο, το οποίο μπορεί να συνοψιστεί σε μία γραμμή:
Δεν αλλάζουμε τη βάση υποβολών που υπάρχουν εκτός του αποθετηρίου μας και κάποια άτομα έχουν δουλέψει πάνω σε αυτά.
Αν ακολουθήσουμε αυτή τη συμβουλή, θα είστε μια χαρά. Αν δεν το κάνουμε, θα γίνουμε μισητοί σε όλη την οικουμένη και θα μας περιφρονήσουν φίλοι και συγγενείς.
Όταν αλλάζουμε τη βάση ενός κλάδου, εγκαταλείπουμε τις υπάρχουσες υποβολές και δημιουργούμε νέες που είναι παρόμοιες μεν, διαφορετικές δε.
Εάν ωθήσουμε υποβολές κάπου και άλλοι τις τραβήξουν και βασίσουν τη δουλειά τους σε αυτές και στη συνέχεια ξαναγράψουμε αυτές τις υποβολές με την git rebase και τις ωθήσουμε ξανά, οι συνεργάτες μας θα πρέπει να ξανασυγχωνεύσουν τη δουλειά τους και τα πράγματα θα μπλέξουν όταν προσπαθήσουμε να έλξουμε τη δουλειά τους στη δική μας.
Ας δούμε ένα παράδειγμα με το οποίο η αλλαγή της βάσης κάποιας εργασίας που έχουμε ήδη κοινοποιήσει σε άλλους μπορεί να προκαλέσει προβλήματα. Ας υποθέσουμε ότι κλωνοποιούμε από έναν κεντρικό διακομιστή και στη συνέχεια κάνουμε κάποιες αλλαγές. Το ιστορικό της υποβολής μας μοιάζει με αυτό:
Τώρα, κάποιος άλλος κάνει και άλλη δουλειά που περιλαμβάνει συγχώνευση και ωθεί τη δουλειά του στον κεντρικό διακομιστή. Την ανακτούμε και συγχωνεύουμε τον νέο απομακρυσμένο κλάδο στη δουλειά μας, κάνοντας το ιστορικό μας να μοιάζει με αυτό:
Στη συνέχεια, αυτός που ώθησε τη συγχωνευμένη δουλειά αποφασίζει να επιστρέψει και να αλλάξει τη βάση της εργασίας του· κάνει git push --force για να επανεγγράψει το ιστορικό στον διακομιστή.
Στη συνέχεια, ανακτούμε από τον διακομιστή και φέρνουμε τις νέες υποβολές.
Τώρα έχουμε μπλέξει άσχημα.
Εάν κάνουμε git pull, θα δημιουργήσουμε μια υποβολή συγχώνευσης που συμπεριλαμβάνει και τις δύο γραμμές του ιστορικού και το αποθετήριό μας θα μοιάζει με αυτό:
Αν το ιστορικό μας μοιάζει με το παραπάνω και τρέξουμε git log, θα δούμε δύο υποβολές που έχουν τον ίδιο συγγραφέα, ημερομηνία και μήνυμα, κάτι που μπορεί να προκαλέσει σύγχυση.
Επιπλέον, αν ωθήσουμε αυτό το ιστορικό πίσω στον διακομιστή, θα επαναφέρουμε όλες εκείνες τις επανατοποθετημένες υποβολές στον κεντρικό εξυπηρετητή, κάτι που θα μπερδέψει ακόμα περισσότερο τους υπόλοιπους.
Είναι αρκετά σίγουρο ότι ο άλλος προγραμματιστής δεν θέλει οι C4 και C6 να βρίσκονται στο ιστορικό· άλλωστε αυτός είναι ο λόγος για τον οποίο έκανε την αλλαγή της βάσης.
Επανατοποθέτηση σε επανατοποθετημένες υποβολές
Αν παρόλα αυτά, όντως βρεθούμε σε μια τέτοια κατάσταση, το Git διαθέτει κάποια μαγικά κόλπα που θα μπορούν να μας βοηθήσουν. Εάν κάποιος από την ομάδα μας ωθεί εξαναγκαστικά αλλαγές που επανεγγράφουν εργασία στην οποία βασίσαμε τη δική μας δουλειά, το πρόβλημά μας ανάγεται στο να εντοπίσουμε τι είναι δικό μας και τι έχει ξαναγραφτεί από αυτό.
Το Git εκτός από το άθροισμα ελέγχου SHA-1 υπολογίζει επίσης ένα άθροισμα ελέγχου που βασίζεται ακριβώς στο επίθεμα που εισήχθη με την υποβολή. Αυτό ονομάζεται “patch-id” (ταυτότητα επιθέματος).
Αν τραβήξουμε εργασία που ξαναγράφτηκε και αλλάξουμε τη βάση του δικού μας κλάδου ώστε αυτός να βασίζεται πάνω στις νέες υποβολές του συνεργάτη μας, το Git μπορεί συχνά να καταλάβει ποιες αλλαγές είναι μόνο δικές μας και να τις εφαρμόσει ξανά στον νέο κλάδο.
Για παράδειγμα, αν στο προηγούμενο σενάριο, όταν βρισκόμαστε στο Κάποιος ωθεί επανατοποθετημένες υποβολές, εγκαταλείποντας τις υποβολές στις οποίες έχουμε βασίσει τη δουλειά μας αντί να κάνουμε συγχώνευση, τρέξουμε git rebase teamone/master, το Git:
-
Θα προσδιορίσει ποια δουλειά βρίσκεται μόνον στον κλάδο μας (
C2,C3,C4,C6,C7) -
Θα προσδιορίσει ποιες υποβολές δεν είναι υποβολές συγχώνευσης (
C2,C3,C4) -
Θα προσδιορίσει ποιες υποβολές δεν έχουν ξαναγραφτεί στον κλάδο στόχο (μόνο οι
C2καιC3, δεδομένου ότι ηC4είναι το ίδιο επίθεμα με τηνC4') -
Θα εφαρμόσει αυτές τις υποβολές στον κλάδο
teamone/master.
Έτσι, αντί για το αποτέλεσμα που βλέπουμε στην Συγχώνευση της ίδιας εργασίας σε μία νέα υποβολή συγχώνευσης, θα καταλήγαμε σε κάτι που μοιάζει πιο πολύ με την Επανατοποθέτηση πάνω σε εξαναγκασμένα επανατοποθετημένη εργασία.
Αυτό θα έχει το επιθυμητό αποτέλεσμα μόνον εάν οι 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 ώστε να καταπραΰνουμε λίγο τον πόνο που θα ακολουθήσει.
Σύγκριση αλλαγής βάσης και συγχώνευσης
Τώρα που έχουμε δει την αλλαγή βάσης και τη συγχώνευση σε δράση, ίσως να αναρωτηθούμε ποια διαδικασία είναι καλύτερη. Πριν απαντήσουμε σε αυτό το ερώτημα, ας θυμηθούμε τι ακριβώς είναι το ιστορικό.
Μια θεώρηση του πράγματος είναι ότι το ιστορικό των υποβολών του αποθετηρίου μας είναι καταγραφή όσων πραγματικά συνέβησαν. Είναι ένα ιστορικό έγγραφο, πολύτιμο από μόνο του και δεν πρέπει να παραβιάζεται. Από αυτή τη σκοπιά, η αλλαγή του ιστορικού των υποβολών αποτελεί σχεδόν βλασφημία· λέμε ψέματα για το τι συνέβη πραγματικά. Τι γίνεται, λοιπόν, αν υπάρχει μια μπουρδουκλωμένη σειρά υποβολών συγχώνευσης; Αυτός είναι ο τρόπος με τον οποίο συνέβησαν και το αποθετήριο πρέπει να τη διατηρήσει για πάντα.
Η αντίθετη άποψη είναι ότι το ιστορικό της υποβολής είναι η ένα αφήγημα του πώς έγινε το έργο μας.
Δεν θα δημοσιεύαμε το προσχέδιο ενός βιβλίου, οπότε γιατί να δείξουμε όλη την τσαπατσούλικη δουλειά;
Όταν εργάζομαστε σε ένα έργο, ίσως χρειαστούμε μια καταγραφή όλων λανθασμένων βημάτων και των αδιεξόδων, αλλά όταν φτάνει η ώρα να δείξουμε τη δουλειά μας στον υπόλοιπο κόσμο, ίσως θέλουμε να πούμε μια πιο συνεκτική ιστορία του πώς πήγαμε από το Α στο Β.
Όσοι βρίσκονται σε αυτό το στρατόπεδο χρησιμοποιούν εργαλεία όπως rebase και filter-branch για να ξαναγράψουν τις υποβολές τους πριν αυτές συγχωνευτούν στον κύριο κλάδο.
Χρησιμοποιούν εργαλεία όπως rebase και filter-branch για να αφηγηθούν την ιστορία με τρόπο που είναι ο καλύτερος για τους μελλοντικούς αναγνώστες.
Τώρα, στο ερώτημα τι είναι καλύτερο, η συγχώνευση ή αλλαγή βάσης; Ας ελπίσουμε ότι βλέπετε ότι η απάντηση δεν είναι τόσο απλή. Το Git είναι ένα ισχυρό εργαλείο και μας επιτρέπει να κάνουμε πολλά πράγματα στο ιστορικό ή με το ιστορικό, αλλά κάθε ομάδα και κάθε έργο είναι διαφορετικά. Εφόσον γνωρίζουμε πώς λειτουργούν και οι δύο αυτές διαδικασίες, μπορούμε να αποφασίσουμε ποια είναι η καλύτερη σε κάθε περίσταση.
Μπορούμε να συνδυάσουμε και τα δύο: να επανατοποθετούμε τοπικές αλλαγές που δεν έχουμε κοινοποιήσει σε άλλους, προκειμένου να καθαρίσουμε το ιστορικό μας, αλλά ποτέ να μην επανατοποθετούμε τίποτα που έχουμε ήδη ωθήσει κάπου.