Chapters ▾ 2nd Edition

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

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

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

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

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

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

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

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

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

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

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

Note

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

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

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

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

$ git add README

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

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git restore --staged <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” βρίσκεται κάτω από την κατηγορία “Changes not staged for commit” — που σημαίνει ότι ένα αρχείο υπό παρακολούθηση έχει τροποποιηθεί στον κατάλογο εργασίας, αλλά δεν έχει τοποθετηθεί ακόμα στο στάδιο καταχώρησης. Για να το τοποθετήσουμε στο στάδιο καταχώρησης θα πρέπει να εκτελέσουμε την εντολή git add. Η εντολή git add έχει πολλές λειτουργίες· τη χρησιμοποιoύμε για να ξεκινήσουμε την παρακολούθηση καινούργιων αρχείων, για να τοποθετήσουμε αρχεία στο στάδιο καταχώρησης αλλά και για άλλες λειτουργίες όπως το να επισημάνουμε αρχεία που προέρχονται από συγκρούσεις συγχώνευσης (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

Σύντομο Status

Αν και το αποτέλεσμα της εντολής 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 έχει τροποποιηθεί, τοποθετηθεί στο στάδιο καταχώρησης και στη συνέχεια τροποποιήθηκε ξανά, που σημαίνει ότι υπάρχουν κάποιες αλλαγές που έχουν τοποθετηθεί στο στάδιο καταχώρησης και άλλες που δεν έχουν τοποθετηθεί στο στάδιο καταχώρησης.

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

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

$ cat .gitignore
*.[oa]
*~

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

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

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

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

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

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

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

Τα μοτίβα αυτά μοιάζουν με απλοποιημένες κανονικές εκφράσεις (regular expressions) που χρησιμοποιούν τα κέλυφοι (shells). Ένας αστερίσκος (*) αντιστοιχεί σε μηδέν ή περισσότερους χαρακτήρες· το [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. Θα καλύψουμε την εντολή αυτή πιο αναλυτικά αργότερα, αλλά το πιθανότερο είναι ότι θα τη χρησιμοποιoύμε πιο συχνά για να απαντήσουμε σε αυτές τις δύο ερωτήσεις: Τι έχουμε αλλάξει και δεν έχουμε ακόμα τοποθετήσει στο στάδιο καταχώρησης; Και τι έχουμε τοποθετήσει στο στάδιο καταχώρησης και είναι έτοιμο να υποβληθεί; Ενώ η εντολή 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

Όταν το κάνουμε, θα ξεκινήσει ο προεπιλεγμένος επεξεργαστής κειμένου μας.

Note

Αυτός είναι καθορισμένος από τη μεταβλητή περιβάλλοντος (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 που προέκυψαν από μεταγλώττιση. Για να το κάνουμε αυτό, χρησιμοποιoύμε την επιλογή --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

Παρόλα αυτά, είναι ισοδύναμο με το να εκτελέσουμε το εξής:

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

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