-
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 stash και clean
- 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 στο Eclipse
- A1.4 Το Git στο Bash
- A1.5 Το Git στο Zsh
- A1.6 Το Git στο Powershell
- A1.7 Ανακεφαλαίωση
-
A2. Appendix B: Ενσωμάτωση του Git στις εφαρμογές μας
- A2.1 Γραμμή εντολών Git
- A2.2 Libgit2
- A2.3 JGit
-
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 Εντολές διοχέτευσης
7.13 Εργαλεία του Git - Replace
Replace
Τα αντικείμενα του Git είναι αμετάβλητα αλλά παρέχουν έναν ενδιαφέροντα τρόπο για να προσποιούνται ότι αντικαθιστούν αντικείμενα με άλλα αντικείμενα στη βάση δεδομένων του.
Η εντολή replace
μάς επιτρέπει να ορίσουμε ένα αντικείμενο στο Git και να λέμε, “κάθε φορά που βλέπουμε αυτό, θα υποκριθούμε ότι είναι κάτι άλλο”.
Αυτό είναι συνήθως χρήσιμο για την αντικατάσταση μιας υποβολής στο ιστορικό μας με μία άλλη.
Για παράδειγμα, ας υποθέσουμε ότι έχουμε ένα τεράστιο ιστορικό και θέλουμε να χωρίσουμε το αποθετήριό μας σε ένα σύντομο, πρόσφατο ιστορικό για τους νέους προγραμματιστές και ένα πολύ εκτενέστερο για όσους ενδιαφέρονται για εξόρυξη δεδομένων.
Μπορούμε να μεταμοσχεύσουμε το ένα ιστορικό στο άλλο με την replace
, αντικαθιστώντας την παλιότερη υποβολή στη νέα γραμμή παραγωγής με την πιο πρόσφατη υποβολή στην παλαιότερη.
Αυτό είναι ωραίο διότι σημαίνει ότι δεν χρειάζεται να ξαναγράψουμε κάθε υποβολή στο νέο ιστορικό, όπως θα έπρεπε κανονικά να κάνουμε για να τα ενώσουμε μαζί (επειδή η γονικότητα επηρεάζει τον αριθμό SHA-1).
Ας το δοκιμάσουμε.
Ας πάρουμε ένα υπάρχον αποθετήριο, το χωρίζουμε σε δύο αποθετήρια, ένα πρόσφατο και ένα ιστορικό και στη συνέχεια θα δούμε πώς μπορούμε να τα ξανασυνδυάσουμε χωρίς να τροποποιήσουμε τις τιμές SHA-1 των νέων αποθετηρίων μέσω replace
.
Θα χρησιμοποιήσουμε ένα απλό αποθετήριο με πέντε απλές υποβολές:
$ git log --oneline
ef989d8 fifth commit
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Θέλουμε να το χωρίσουμε σε δύο ιστορικά. Το ένα ιστορικό πηγαίνει από την υποβολή ένα στην υποβολή τέσσερα —αυτό θα είναι το “αρχαίο” ιστορικό. Το δεύτερο θα είναι μόνον οι υποβολές τέσσερα και πέντε —αυτό θα είναι το πρόσφατο ιστορικό.

Η δημιουργία του “αρχαίου” ιστορικού είναι εύκολη, μπορούμε απλά να φτιάξουμε έναν κλάδο στην υποβολή τέσσερα και στη συνέχεια να ωθήσουμε αυτόν τον κλάδο στον κύριο κλάδο ενός νέου απομακρυσμένου αποθετηρίου.
$ 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

Τώρα μπορούμε να ωθήσουμε τον νέο κλάδο history
στον κλάδο master
του νέου αποθετηρίου μας:
$ 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
Το ιστορικό μας είναι πλέον δημοσιευμένο. Τώρα το δυσκολότερο κομμάτι είναι να κουτσουρέψουμε το πρόσφατο ιστορικό μας, για να το μικρύνουμε. Πρέπει να υπάρχει μια επικάλυψη με το “αρχαίο” ιστορικό, ώστε να μπορέσουμε να αντικαταστήσουμε μια υποβολή στο ένα με μία ισοδύναμη υποβολή στο άλλο, συνεπώς θα καρτήσουμε μόνον τις υποβολές τέσσερα και πέντε (η υποβολή τέσσερα είναι κοινή).
Είναι χρήσιμο σε αυτήν την περίπτωση να δημιουργήσουμε μια υποβολή που θα λειτουργήσει σαν βάση που έχει οδηγίες σχετικά με τον τρόπο επέκτασης του ιστορικού, έτσι ώστε οι υπόλοιποι προγραμματιστές να γνωρίζουν τι πρέπει να κάνουν αν φτάσουν στην πρώτη υποβολή στο κουτσουρεμένο ιστορικό και θέλουν να πάνε πιο πίσω. Συνεπώς αυτό που θα κάνουμε είναι να δημιουργήσουμε ένα αρχικό αντικείμενο υποβολής με οδηγίες ως το σημείο-βάση και στη συνέχεια να αλλάξουμε τη βάση των υπόλοιπων υποβολών (τέσσερα και πέντε) πάνω σε αυτό.
Για να γίνει αυτό, πρέπει να επιλέξουμε ένα σημείο στο οποίο θα γίνει η διακλάδωση· αυτό για εμάς είναι η τρίτη υποβολή, δηλαδή, η 9c68fdc
στη γλώσσα του SHA.
Έτσι, η υποβολή-βάση θα διακλαδώνεται από αυτό το δέντρο.
Μπορούμε να δημιουργήσουμε την υποβολή μας χρησιμοποιώντας την εντολή commit-tree
, η οποία παίρνει ακριβώς ένα δέντρο και θα μας επιστρέψει τον αριθμό SHA-1 ενός ολοκαίνουριου ορφανού αντικειμένου υποβολής.
$ echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Note
|
Η εντολή |

Τώρα, λοιπόν, που έχουμε μια υποβολή-βάση, μπορούμε να αλλάξουμε τη βάση του υπόλοιπου του ιστορικού μας πάνω σε αυτήν με την git rebase --onto
.
Το όρισμα --onto
θα είναι το SHA-1 που μόλις πήραμε από την commit-tree
και η νέα βάση θα είναι η τρίτη υποβολή (ο γονέας της πρώτης υποβολής που θέλουμε να κρατήσουμε, δηλαδή της 9c68fdc
):
$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit

Τώρα έχουμε ξαναγράψει το πρόσφατο ιστορικό μας πάνω από μια υποβολή-βάση που περιέχει οδηγίες για το πώς να ανακατασκευαστεί ολόκληρο το ιστορικό, αν το θελήσουμε. Μπορούμε να ωθήσουμε αυτό το νέο ιστορικό σε ένα νέο έργο και τώρα όταν κάποιος κλωνοποιεί αυτό το αποθετήριο, θα δει μόνο τις πιο πρόσφατες δύο υποβολές και έπειτα μια υποβολή-βάση με οδηγίες.
Ας αλλάξουμε τώρα ρόλο και ας δούμε το πράγμα από τη σκοπιά ενός συνεργάτη που κλωνοποιεί το έργο για πρώτη φορά και θέλει το πλήρες ιστορικό. Για να αποκτήσει τα δεδομένα του πλήρους ιστορικού μετά την κλωνοποίηση αυτού του κουτσουρεμένου αποθετηρίου, θα πρέπει να προσθέσει ένα δεύτερο απομακρυσμένο αποθετήριο για το “αρχαίο” αποθετήριο και να ανακτήσει:
$ 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
.
$ 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
:
$ git replace 81a708d c6e1e95
Αν κοιτάξουμε τώρα το ιστορικό του κλάδου master
, φαίνεται να μοιάζει με αυτό:
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Καλή φάση, σωστά; Χωρίς να χρειάζεται να αλλάξουμε όλα τα SHA-1s upstream, μπορέσαμε να αντικαταστήσουμε μια υποβολή στο ιστορικό μας με μια εντελώς διαφορετική υποβολή και όλα τα συνήθη εργαλεία (bisect
, blame
κ.λπ.) θα δουλεύουν με τον τρόπο με τον οποίο θα αναμέναμε.

Κάτι ενδιαφέρον είναι ότι εξακολουθεί να εμφανίζεται η 81a708d
ως SHA-1, παρόλο που στην πραγματικότητα χρησιμοποιούνται τα δεδομένα της c6e1e95
με την οποία την αντικαταστήσαμε.
Ακόμα και αν εκτελέσουμε μια εντολή όπως η cat-file
, θα μας δείξει τα αντικατασταθέντα δεδομένα:
$ 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
όπως δηλώνεται εδώ.
Κάτι ακόμα ενδιαφέρον είναι ότι αυτά τα δεδομένα διατηρούνται στις αναφορές μας:
$ 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
Αυτό σημαίνει ότι είναι εύκολο να μοιραστούμε την αντικατάστασή μας με άλλους, διότι μπορούμε να την ωθήσουμε στον διακομιστή μας και οι άλλοι μπορούν εύκολα να την κατεβάσουν. Αυτό δεν είναι και τόσο χρήσιμο στο σενάριο μεταμόσχευσης ιστορικού που είδαμε (αν επρόκειτο όλοι να κατεβάσουν και τα δύο ιστορικά ούτως ή άλλως, γιατί μπήκαμε στον κόπο να τα χωρίσουμε;) αλλά μπορεί να είναι χρήσιμο και σε άλλες περιπτώσεις.