Git Branch (Κλάδος) vs Git WorkTree (Χώρος Εργασίας / Φάκελος)

Git Branch (Κλάδος) vs Git WorkTree (Χώρος Εργασίας / Φάκελος)

Όταν αρχίζουμε να χρησιμοποιούμε Git, branches και worktrees, κάποια πράγματα φαίνονται πιο περίπλοκα από όσο είναι. Η κατάσταση γίνεται ακόμη πιο μπερδεμένη όταν εμφανίζονται αρχεία όπως το .env και τα database connection strings. Ας τα δούμε λοιπόν με ένα πολύ απλό παράδειγμα.

Πρώτα: τι είναι ένα Git branch;

Φαντάσου ότι φτιάχνεις ένα παιχνίδι με LEGO.

Έχεις την κανονική έκδοση του παιχνιδιού σου:

main

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

Δημιουργείς λοιπόν:

feature-castle

Αυτό είναι ένα branch.

Το branch δεν είναι δεύτερος φάκελος στον υπολογιστή σου. Είναι ουσιαστικά ένας δείκτης προς μια συγκεκριμένη κατάσταση του κώδικα.

Τα branches είναι ελαφριοί δείκτες προς commits του Git repository.

Τι γίνεται όταν αλλάζω branch;

Ας πούμε ότι έχεις αυτόν τον φάκελο:

MyProject/

και βρίσκεσαι στο:

main

Αν γράψεις:

git switch feature-castle

παραμένεις μέσα στον ίδιο φάκελο:

MyProject/

Το Git όμως αλλάζει τα αρχεία ώστε να αντιστοιχούν στο άλλο branch.

Δηλαδή:

MyProject/
    ↓
   main

git switch feature-castle

MyProject/
    ↓
feature-castle

Έχεις έναν φάκελο, αλλά μπορείς να αλλάζεις ποιο branch εμφανίζεται μέσα του.

Και τότε τι είναι το worktree;

Το Git worktree κάνει κάτι διαφορετικό.

Σου επιτρέπει να έχεις πολλά branches ανοιχτά ταυτόχρονα, μέσα σε διαφορετικούς φακέλους.

Για παράδειγμα:

MyProject/
    main

MyProject-login/
    feature-login

MyProject-payments/
    feature-payments

Όλοι αυτοί οι φάκελοι χρησιμοποιούν το ίδιο Git repository και το ίδιο ιστορικό.

Όμως κάθε worktree έχει τα δικά του πραγματικά αρχεία και τις δικές του μη αποθηκευμένες αλλαγές.

Αυτό είναι ιδιαίτερα χρήσιμο όταν χρησιμοποιούμε AI coding agents.

Μπορούμε να έχουμε:

Agent 1 → feature-login
Agent 2 → feature-payments
Agent 3 → bugfix-email

χωρίς ο ένας agent να αλλάζει συνεχώς τα αρχεία του άλλου.

Branch και worktree λοιπόν δεν είναι το ίδιο

Η πιο απλή εξήγηση είναι αυτή:

Το branch είναι η έκδοση της δουλειάς.

Το worktree είναι ο φάκελος όπου δουλεύεις πάνω σε αυτή την έκδοση.

Ένα branch είναι λογική έννοια.

Ένα worktree είναι πραγματικός φάκελος στον δίσκο σου.

Το VS Code χρησιμοποιεί ακριβώς αυτή τη λογική για παράλληλη εργασία σε διαφορετικά branches.


Και τώρα έρχεται το περίεργο: τι γίνεται με το .env;

Εδώ βρίσκεται ένα σημείο που μπορεί εύκολα να προκαλέσει προβλήματα.

Ένα .env αρχείο συνήθως περιέχει ρυθμίσεις που αφορούν το συγκεκριμένο μηχάνημα ή περιβάλλον.

Για παράδειγμα:

DB_HOST=localhost
DB_NAME=myproject
DB_USER=root
DB_PASSWORD=supersecret

Μπορεί επίσης να περιέχει:

OPENAI_API_KEY=xxxx
STRIPE_SECRET_KEY=xxxx

Αυτές οι πληροφορίες δεν πρέπει κανονικά να ανεβαίνουν στο GitHub.

Γι' αυτό συνήθως έχουμε:

.env

Το Git τότε αγνοεί το συγκεκριμένο αρχείο.

Τι γίνεται με το .env όταν αλλάζω branch;

Ας έχουμε:

MyProject/
    .env
    Program.cs
    README.md

και το .env βρίσκεται στο .gitignore.

Βρίσκεσαι στο:

main

και κάνεις:

git switch feature-login

Το Git αλλάζει τα αρχεία που παρακολουθεί.

Το .env όμως συνήθως παραμένει εκεί, επειδή δεν ανήκει στο Git.

Άρα:

main
  │
  │ git switch
  ↓
feature-login

ίδιος φάκελος
ίδιο τοπικό .env

Αυτό είναι πολύ σημαντικό.

Τα branches μπορεί να αλλάζουν, αλλά το τοπικό .env παραμένει συνδεδεμένο με τον φάκελο, όχι με το branch.


Τι γίνεται όμως με ένα worktree;

Εδώ αλλάζει η κατάσταση.

Ας έχεις:

MyProject/

και μέσα:

.env

Δημιουργείς τώρα ένα δεύτερο worktree:

git worktree add ../MyProject-login feature-login

Θα αποκτήσεις:

MyProject/
    .env

MyProject-login/

Το .env δεν αντιγράφεται αυτόματα στο δεύτερο worktree όταν είναι ignored και untracked.

Γιατί;

Επειδή το Git δημιουργεί το δεύτερο worktree χρησιμοποιώντας τα αρχεία που γνωρίζει το repository.

Το .env όμως το έχουμε πει στο Git να αγνοεί.

Άρα μπορεί να καταλήξουμε με:

MyProject/
    .env ✅

MyProject-login/
    .env ❌

Το πρόγραμμα στο δεύτερο worktree μπορεί τότε να ξεκινήσει και να πει:

Database connection failed

ή:

Missing API key

Δεν χάλασε το branch.

Δεν χάλασε το Git.

Απλώς ο νέος φάκελος δεν έχει τις απαραίτητες τοπικές ρυθμίσεις.


Και τι γίνεται με το connection string;

Ένα connection string είναι ουσιαστικά η «διεύθυνση και τα κλειδιά» που χρειάζεται η εφαρμογή για να βρει τη βάση δεδομένων.

Για παράδειγμα:

Server=localhost;
Database=CodeCraft;
User Id=developer;
Password=secret;

Μπορούμε να το αποθηκεύσουμε ως environment variable:

DATABASE_URL=Server=localhost;Database=CodeCraft;User Id=developer;Password=secret;

ή σε εφαρμογές .NET με ονομασία όπως:

ConnectionStrings__DefaultConnection

Οι environment variables μπορούν να αντικαθιστούν αντίστοιχες τιμές της configuration του ASP.NET Core.

Άρα ισχύει ακριβώς το ίδιο πρόβλημα.

Αν το connection string βρίσκεται στο τοπικό .env:

Project-main/
    .env
        DATABASE_URL=...

το νέο worktree:

Project-feature/

δεν αποκτά αυτόματα αυτό το .env.

Θα πρέπει να του δοθεί με κάποιον ασφαλή τρόπο.


Τι δεν πρέπει να κάνουμε

Η εύκολη λύση φαίνεται να είναι:

«Ας βάλω το .env μέσα στο Git για να πηγαίνει παντού».

Αυτό όμως είναι συνήθως πολύ κακή ιδέα.

Ένα .env μπορεί να περιέχει:

Database passwords
API keys
SMTP passwords
Stripe keys
JWT secrets
Cloud credentials

Αν γίνει commit, αυτά μπορεί να καταλήξουν στο GitHub και στο ιστορικό του repository.

Ακόμη και αν διαγράψουμε αργότερα το αρχείο, το secret μπορεί να υπάρχει σε παλαιότερο commit.

Η Microsoft επίσης συνιστά τα secrets να αποθηκεύονται έξω από το project και να μην γίνονται commit στο source repository.


Η καλύτερη πρακτική

Στο Git μπορούμε να έχουμε ένα αρχείο:

.env.example

που περιέχει μόνο τα ονόματα:

DB_HOST=
DB_NAME=
DB_USER=
DB_PASSWORD=
OPENAI_API_KEY=

Το .env.example μπορεί να βρίσκεται κανονικά στο Git.

Το πραγματικό:

.env

παραμένει στο:

 

Έτσι ένας developer ή agent βλέπει τι χρειάζεται η εφαρμογή, χωρίς να βλέπει τα πραγματικά passwords.

Με worktrees λοιπόν έχουμε:

repository
│
├── main worktree
│   └── .env
│
├── login worktree
│   └── .env
│
└── payment worktree
    └── .env

Τα τρία .env μπορούν να έχουν ίδιες ή διαφορετικές τιμές.

Για παράδειγμα:

main
    ↓
Database: development_main

login
    ↓
Database: development_login

payments
    ↓
Database: development_payments

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


Ειδικά για .NET

Στο .NET υπάρχει ακόμη καλύτερη επιλογή για development secrets.

Η Microsoft παρέχει το User Secrets σύστημα.

Για παράδειγμα:

dotnet user-secrets set "ConnectionStrings:DefaultConnection" "..."

Το secret αποθηκεύεται έξω από τον φάκελο του project.

Έτσι δεν χρειάζεται να βρίσκεται ούτε:

appsettings.json

ούτε:

.env

ούτε στο Git repository.

Η Microsoft προτείνει User Secrets για development και ασφαλέστερες secret stores, όπως Azure Key Vault, για production περιβάλλοντα.


Η εικόνα που πρέπει να θυμόμαστε

Φαντάσου ένα σχολείο.

Το Git repository είναι η βιβλιοθήκη του σχολείου.

Τα branches είναι διαφορετικές εκδόσεις του ίδιου βιβλίου.

Τα worktrees είναι διαφορετικά θρανία όπου έχουμε ανοιχτές διαφορετικές εκδόσεις.

Και το .env;

Το .env είναι ένα χαρτάκι με passwords που έχει αφήσει κάθε μαθητής πάνω στο δικό του θρανίο.

Όταν αλλάζεις βιβλίο στο ίδιο θρανίο, το χαρτάκι παραμένει εκεί.

Όταν όμως πας σε καινούριο θρανίο, το χαρτάκι δεν εμφανίζεται μαγικά.

Και σίγουρα δεν θέλουμε να βάλουμε το χαρτάκι με τα passwords μέσα στο βιβλίο της βιβλιοθήκης.

Με μία πρόταση

Branch = ποια έκδοση του κώδικα δουλεύω.

Worktree = σε ποιον φάκελο δουλεύω.

.env = τοπικές ρυθμίσεις του συγκεκριμένου φακέλου.

Connection string = μία από αυτές τις ρυθμίσεις, συχνά ευαίσθητη.

Αν καταλάβουμε αυτές τις τέσσερις έννοιες, τα Git worktrees παύουν να φαίνονται περίπλοκα.

Πώς σου φάνηκε;

Efthimios Theotokatos

Η συντακτική ομάδα του CodeCraft καλύπτει τεχνολογία, AI, hardware και software.

Σχόλια

Φόρτωση σχολίων…