Git Pull Force - Πώς να αντικαταστήσετε τις τοπικές αλλαγές με το Git

Όταν μαθαίνετε να κωδικοποιείτε, αργά ή γρήγορα θα μάθετε επίσης για τα Συστήματα Ελέγχου Έκδοσης. Και ενώ υπάρχουν πολλά ανταγωνιστικά εργαλεία σε αυτόν τον χώρο, ένα από αυτά είναι το de facto πρότυπο που χρησιμοποιείται από σχεδόν όλους στον κλάδο. Είναι τόσο δημοφιλές που υπάρχουν εταιρείες που χρησιμοποιούν το όνομά της στην επωνυμία τους. Μιλάμε φυσικά για το Git.

Ενώ το Git είναι ένα ισχυρό εργαλείο, η δύναμή του είναι καλά κρυμμένη. Υπάρχουν μερικές βασικές έννοιες που πρέπει να καταλάβετε για να γίνετε πραγματικά ικανοί στο Git. Τα καλά νέα είναι ότι μόλις τα μάθετε, δεν θα συναντήσετε ποτέ προβλήματα που δεν μπορείτε να ξεφύγετε.

Η τυπική ροή εργασίας

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

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

Όταν ολοκληρώσετε μια εργασία, ήρθε η ώρα να συγχρονίσετε με το απομακρυσμένο αποθετήριο. Θέλετε να τραβήξετε τις απομακρυσμένες αλλαγές για να συμβαδίσετε με την πρόοδο του έργου και θέλετε να προωθήσετε τις τοπικές αλλαγές για να μοιραστείτε την εργασία σας με άλλους.

Τοπικές αλλαγές

Όλα είναι καλά όταν εσείς και η υπόλοιπη ομάδα σας εργάζεστε σε εντελώς ξεχωριστά αρχεία. Ό, τι κι αν συμβεί, δεν θα πατάτε τα πόδια του άλλου.

Ωστόσο, υπάρχουν στιγμές που εσείς και οι συμπαίκτες σας εισαγάγετε ταυτόχρονα αλλαγές στο ίδιο μέρος. Και συνήθως ξεκινούν τα προβλήματα.

Έχετε εκτελέσει ποτέ git pullμόνο για να δείτε το φόβο error: Your local changes to the following files would be overwritten by merge:; Αργά ή γρήγορα, όλοι αντιμετωπίζουν αυτό το πρόβλημα.

Αυτό που είναι πιο συγκεχυμένο εδώ είναι ότι δεν θέλετε να συγχωνεύσετε τίποτα, απλά τραβήξτε, σωστά; Στην πραγματικότητα, το τράβηγμα είναι λίγο πιο περίπλοκο από ό, τι θα μπορούσατε να σκεφτείτε.

Πόσο ακριβώς λειτουργεί το Git Pull;

Το Pull δεν είναι μία λειτουργία. Αποτελείται από τη λήψη δεδομένων από τον απομακρυσμένο διακομιστή και στη συνέχεια τη συγχώνευση των αλλαγών με το τοπικό αποθετήριο. Αυτές οι δύο λειτουργίες μπορούν να εκτελεστούν χειροκίνητα εάν θέλετε:

git fetch git merge origin/$CURRENT_BRANCH

Το origin/$CURRENT_BRANCHμέρος σημαίνει ότι:

  • Το Git θα συγχωνεύσει τις αλλαγές από το απομακρυσμένο αποθετήριο που ονομάζεται origin(αυτό από το οποίο κλωνοποιήσατε)
  • που έχουν προστεθεί στο $CURRENT_BRANCH
  • που δεν υπάρχουν ήδη στο τοπικό σας κατάστημα

Δεδομένου ότι το Git εκτελεί συγχωνεύσεις μόνο όταν δεν υπάρχουν αλλαγές χωρίς δέσμευση, κάθε φορά που εκτελείτε git pullμε μη δεσμευμένες αλλαγές θα μπορούσε να σας προκαλέσει πρόβλημα. Ευτυχώς, υπάρχουν τρόποι να ξεφύγετε από το πρόβλημα σε ένα κομμάτι!

Είμαστε οικογένεια

Διαφορετικές προσεγγίσεις

Όταν έχετε ασυμπίεστες τοπικές αλλαγές και εξακολουθείτε να θέλετε να τραβήξετε μια νέα έκδοση από τον απομακρυσμένο διακομιστή, η θήκη χρήσης συνήθως εμπίπτει σε ένα από τα ακόλουθα σενάρια. Είτε:

  • δεν ενδιαφέρεστε για τις τοπικές αλλαγές και θέλετε να τις αντικαταστήσετε,
  • σας ενδιαφέρει πολύ οι αλλαγές και θα θέλατε να τις εφαρμόσετε μετά τις απομακρυσμένες αλλαγές,
  • θέλετε να κάνετε λήψη των απομακρυσμένων τροποποιήσεων αλλά να μην τις εφαρμόσετε ακόμη

Κάθε μία από τις προσεγγίσεις απαιτεί διαφορετική λύση.

Δεν ενδιαφέρεστε για τις τοπικές αλλαγές

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

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

git fetch git reset --hard HEAD git merge origin/$CURRENT_BRANCH

Εάν δεν θέλετε να πληκτρολογήσετε το όνομα υποκατάστημα κάθε φορά που εκτελείτε αυτήν την εντολή, Git έχει μια ωραία συντόμευση που δείχνει σε ανάντη κλάδου: @{u}. Ένας κλάδος ανάντη είναι ο κλάδος στο απομακρυσμένο αποθετήριο από το οποίο ωθείτε και ανακτάτε.

Έτσι θα μοιάζουν οι παραπάνω εντολές με τη συντόμευση:

git fetch git reset --hard HEAD git merge '@{u}'

Παραθέτουμε τη συντόμευση στο παράδειγμα για να αποτρέψουμε την ερμηνεία του κελύφους.

Σας ενδιαφέρει πολύ για τις τοπικές αλλαγές

Όταν οι ασυμβίβαστες αλλαγές σας είναι σημαντικές για εσάς, υπάρχουν δύο επιλογές. Μπορείτε να τα δεσμεύσετε και μετά να τα εκτελέσετε git pull, ή μπορείτε να τα αποθηκεύσετε.

Stashing σημαίνει να αφήσετε τις αλλαγές μακριά για να τις επαναφέρετε αργότερα. Για να είμαστε πιο ακριβείς, git stashδημιουργεί μια δέσμευση που δεν είναι ορατή στον τρέχοντα κλάδο σας, αλλά εξακολουθεί να είναι προσβάσιμη από το Git.

Για να επαναφέρετε τις αλλαγές που έχουν αποθηκευτεί στην τελευταία αποθήκευση, χρησιμοποιείτε την git stash popεντολή. Μετά την επιτυχή εφαρμογή των αποθηκευμένων αλλαγών, αυτή η εντολή καταργεί επίσης τη δέσμευση αλλαγής δεδομένου ότι δεν χρειάζεται πλέον.

Η ροή εργασίας θα μπορούσε τότε να μοιάζει με αυτό:

git fetch git stash git merge '@{u}' git stash pop

Από προεπιλογή, οι αλλαγές από το stash θα γίνουν σταδιακά. Εάν θέλετε να τα αποσυνδέσετε, χρησιμοποιήστε την εντολή git restore --staged(εάν χρησιμοποιείτε το Git νεότερο από 2.25.0).

Θέλετε απλώς να κατεβάσετε τις απομακρυσμένες αλλαγές

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

Όπως πιθανώς έχετε καταλάβει, η λήψη των απομακρυσμένων αλλαγών δεν απαιτεί git pullκαθόλου! git fetchείναι αρκετά.

One thing to note is that by default, git fetch will only bring you changes from the current branch. To get all the changes from all the branches, use git fetch --all. And if you'd like to clean up some of the branches that no longer exist in the remote repository, git fetch --all --prune will do the cleaning up!

Some Automation

Have you heard of Git Config? It's a file where Git stores all of the user-configured settings. It resides in your home directory: either as ~/.gitconfig or ~/.config/git/config. You can edit it to add some custom aliases that will be understood as Git commands.

For example, to have a shortcut equivalent to git diff --cached (that shows the difference between the current branch and the staged files), you'd add the following section:

[alias] dc = diff --cached

After that, you can run git dc whenever you wish to review the changes. Going this way, we can set up a few aliases related to the previous use cases.

[alias] pull_force = !"git fetch --all; git reset --hard HEAD; git merge @{u}" pf = pull_force pull_stash = !"git fetch --all; git stash; git merge @{u}; git stash pop"

This way, running git pull_force will overwrite the local changes, while git pull_stash will preserve them.

The Other Git Pull Force

Curious minds may have already discovered that there is such a thing as git pull --force. However, this is a very different beast to what's presented in this article.

It may sound like something that would help us overwrite local changes. Instead, it lets us fetch the changes from one remote branch to a different local branch. git pull --force only modifies the behavior of the fetching part. It is therefore equivalent to git fetch --force.

Like git push, git fetch allows us to specify which local and remote branch do we want to operate on. git fetch origin/feature-1:my-feature will mean that the changes in the feature-1 branch from the remote repository will end up visible on the local branch my-feature. When such an operation modifies the existing history, it is not permitted by Git without an explicit --force parameter.

Just like git push --force allows overwriting remote branches, git fetch --force (or git pull --force) allows overwriting local branches. It is always used with source and destination branches mentioned as parameters. An alternative approach to overwriting local changes using git --pull force could be git pull --force "@{u}:HEAD".

Conclusion

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

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