Oh Shit, Git!?!

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

Ορίστε λοιπόν μερικές σκατό-περιπτώσεις στις οποίες βρέθηκα, και πως εν τέλει κατάφερα να ξεμπλέξω από αυτές, στα λαϊκά.

Oh shit, κάτι έκανα τελείως λάθος, σε παρακαλώ πες μου ότι το git έχει μια μαγική μηχανή του χρόνου!?!

git reflog
# θα σου εμφανίσει μία λίστα με κάθε πράγμα 
# που έγινε στο git, σε όλα τα branches!
# κάθε ένα έχει ένα index HEAD@{index}
# βρες αυτό το οποίο χάλασε τα πάντα
git reset HEAD@{index}
# μαγική μηχανή του χρόνου

Μπορείς να το χρησιμοποιήσεις αυτό για να γυρίσεις σε πράγματα που καταλάθως διέγραψες, ή να διαγράψεις κάποια τα οποία χαλάσανε το repo, ή να κάνεις rollback μετά από ένα κακό merge, ή να γυρίσεις πίσω το χρόνο έως ότου το σημείο μέχρι το οποίο τα πράγματα δούλευαν. Χρησιμοποιώ το reflog ΠΟΛΥ. Βγάζω επειδικτικά το καπέλο στους πολλούς πολλούς πολλούς πολλούς πολλούς ανθρώπους που πρότειναν να προστεθεί αυτή η λειτουργία!

Oh shit, έκανα commit και αμέσως συνειδητοποίησα ότι χρειάζεται να κάνω μια μικρή αλλαγή!

# κάνε την αλλαγή σου
git add . # ή πρόσθεσε ένα-ένα τα αρχεία
git commit --amend --no-edit
# τώρα το τελευταίο σου commit περιέχει την αλλαγή σου!
# ΠΡΟΣΟΧΗ: ποτέ μη κάνεις amend public commits

Αυτό μου συμβαίνει συχνά αν κάνω commit, μετά τρέξω tests/linters... και γαμώ της γης τον άξονα, δεν έβαλα κενό μετά από ένα equals sign. Θα μπορούσες επίσης να κάνεις την αλλαγή ως νέο commit και μετά να κάνεις rebase -i έτσι ώστε να τα ενώσεις και τα δύο μαζί, αλλά το πρώτο είναι πολύ πιο γρήγορο.

Προσοχή: Δεν θα πρέπει να κάνεις ποτέ amend commits τα οποία έχουν γίνει push σε ένα public/shared branch! Κάνε μόνο amend commits τα οποία υπαρχουν σε τοπικό αντίγραφο αλλιώς δεν θα περάσεις καλά.

Oh shit, πρέπει να αλλάξω το τελευταίο commit message!

git commit --amend
# ακολούθησε τα prompts για να αλλάξεις το commit message

Ηλίθια commit message formatting requirements.

Oh shit, καταλάθος έκανα commit κάτι στο master το οποίο θα έπρεπε να είναι σε ξεχωριστό καινούργιο branch!

# δημιούργησε νέο branch από το παρόν state του master
git branch κάποιο-νέο-όνομα-του-branch
# αφαίρεσε το τελευταίο commit από το master branch
git reset HEAD~ --hard
git checkout κάποιο-νέο-όνομα-του-branch
# το commit σου ζει πλέον στο νέο σου branch :)

Σημείωση: αυτό δεν λειτουργεί αν έχεις κάνει ήδη push το commit σε κάποιο δημόσιο/κοινόχρηστο branch, και αν έχεις δοκιμάσει άλλα πράγματα πρώτα, ίσως χρειαστεί να κάνεις git reset HEAD@{number-of-commits-back} έναντι του HEAD~. Άπειρη θλίψης. Επίσης, πολλοί πολλοί πρότειναν έναν υπέροχο τρόπο να το κάνουν αυτό πιο σύντομα τον οποίο ούτε εγώ γνώριζα. Σας ευχαριστώ!

Oh shit, έκανα commit σε λάθος branch!

# αναίρεσε το τελευταίο commit, αλλά άφησε τις αλλαγές διαθέσιμες
git reset HEAD~ --soft
git stash
# πήγαινε στο σωστό branch
git checkout όνομα-του-σωστού-branch
git stash pop
git add . # ή πρόσθεσε ένα-ένα τα αρχεία
git commit -m "το μήνυμα σου";
# τώρα οι αλλαγές σου είναι στο σωστό branch

Πολλοί προτείνανε τη χρήση του cherry-pick για αυτή τη περίσταση, οπότε διάλεξε αυτό που σου βγάζει περισσότερο νόημα!

git checkout name-of-the-correct-branch
# πάρε το τελευταίο commit από το master και βάλτο εδώ
git cherry-pick master
# διέγραψέ το από το master
git checkout master
git reset HEAD~ --hard

Oh shit, προσπάθησα να τρέξω ένα diff αλλά δεν έγινε τίποτα?!

Αν ξέρεις ότι έκανες αλλαγές σε αρχεία, αλλά το diff είναι άδειο, πιθανώς έκανες add τα αρχεία σου στο staging και χρειάζεται να χρησιμοποιήσεις κάποιο special flag.

git diff --staged

File under ¯\_(ツ)_/¯ (ναι, γνωρίζω ότι είναι feature, όχι bug, αλλά είναι στριφνό και μή-προφανές όταν σου συμβαίνει για πρώτη φορά!)

Oh shit, πρέπει να αναιρέσω κάποιο commit από πριν 5 λεπτά!

# βρες το commit που θες να αναιρέσεις
git log
# χρησιμοποίησε τα arrow keys για να σκρολάρεις κάτω στο ιστορικό
# μόλις βρεις το commit σου, σώσε το hash
git revert [το hash που έσωσες]
# το git θα δημιουργήσει ένα νέο commit που αναιρεί το commit σου
# ακολούθησε τα prompts για να κάνεις edit το commit message 
# ή απλά σώσε το commit

Απ' ότι φαίνεται δεν χρειάζεται να εντοπίσεις και να κάνεις copy-paste τα περιεχόμενα ενός παλιού αρχείου σε ένα υπάρχον για να καταφέρεις να αναιρέσεις αλλαγές! Αν έκανες commit κάποιο bug, μπορείς να αναιρέσεις το commit μόνο κάνοντας ένα revert.

Μπορείς επίσης να κάνεις revert ένα αρχείο αντί για ένα ολόκληρο commit! Αλλά φυσικά, επειδή μιλάμε για το git, αυτό θα είναι ένα διαφορετικό σύνολο γαμημένων εντολών...

Oh shit, πρέπει να αναιρέσω τις αλλαγές μου σε ένα αρχείο!

# βρες το hash για ένα commit πριν την αλλαγή του αρχείου
git log
# χρησιμοποίησε τα arrow keys για να σκρολλάρεις στο ιστορικό
# μόλις βρεις το commit σου, σώσε το hash
git checkout [σωσμένο hash] -- path/στο/αρχείο
# η παλιά έκδοση του αρχείου σου θα είναι στο index
git commit -m "Wow, you don't have to copy-paste to undo"

Όταν επιτέλους το βρήκα αυτό ήταν ΣΠΟΥΔΑΙΑ. ΣΠΟΥΔΑΙΑ. Σ-Π-Ο-Υ-Δ-Α-Ι-Α ανακάλυψη. Αλλά σοβαρά, σε ποιο γαμημένο σύμπαν το checkout -- βγάζει νόημα να καταλήξεις ότι είναι ο καλύτερος τρόπος να αναιρέσεις κάποιο αρχείο; :κουνάει-τη-γροθιά-προς-τον-linus-torvalds:

Γάμα το, παραιτούμαι.

cd ..
sudo rm -r γαμημένο-git-repo-dir
git clone https://some.github.url/γαμημένο-git-repo-dir.git
cd γαμημένο-git-repo-dir

Ευχαριστώ τον Eric V. για αυτό. Όλα τα παράπονα για τη χρήση του sudo σε αυτό το αστείο μπορούν να απονεμηθούν σε αυτόν.

Σοβαρά πάντως, αν το branch σου είναι τόσο σαλατοποιημένο ώστε να πρέπει να κάνεις reset τη κατάσταση του repo σου ώστε να είναι η ίδια με το remote repo σε έναν "εγκεκριμένο-git" τρόπο, δοκίμασε αυτό, αλλά πρόσεξε τις καταστροφικές και μη αναστρέψιμες συνέπειες!

# λάβε τη τελευταία κατάσταση του origin
git fetch origin
git checkout master
git reset --hard origin/master
# διέγραψε untracked files και directories
git clean -d --force
# επανέλαβε checkout/reset/clean για κάθε σαλατοποιημένο branch

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

Πολλά ευχαριστώ σε όσους εθελοντικά μετέφρασαν την ιστοσελίδα και σε άλλες γλώσσες, είστε υπέροχοι! Michael Botha (af) · Khaja Md Sher E Alam (bn) · Eduard Tomek (cs) · Moritz Stückler (de) · Franco Fantini (es) · Hamid Moheb (fa) · Senja Jarva (fi) · Michel (fr) · Alex Tzimas (gr) · Elad Leev (he) · Aryan Sarkar (hi) · Ricky Gultom (id) · fedemcmac (it) · Meiko Hori (ja) · Zhunisali Shanabek (kk) · Gyeongjae Choi (ko) · Rahul Dahal (ne) · Martijn ten Heuvel (nl) · Łukasz Wójcik (pl) · Davi Alexandre (pt_BR) · Catalina Focsa (ro) · Daniil Golubev (ru) · Nemanja Vasić (sr) · Björn Söderqvist (sv) · Kitt Tientanopajai (th) · Taha Paksu (tr) · Andriy Sultanov (ua) · Tao Jiayuan (zh) . Με πρόσθετη βοήθεια από την Allie Jones · Artem Vorotnikov · David Fyffe · Frank Taillandier · Iain Murray · Lucas Larson · Myrzabek Azil

Αν θες να βοηθήσεις προσθέτοντας μια μετάφραση στη δικιά σου γλώσσα, κάνε submit ένα PR στο GitHub