Πώς να κατανοήσετε και να επιλύσετε τις συγκρούσεις στο Git

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

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

Ο χειρισμός των συγκρούσεων παραμένει συχνά ένα σκοτεινό, μυστηριώδες μέρος: μια κατάσταση όπου τα πράγματα είναι άσχημα σπασμένα και δεν είναι σαφές πώς να βγούμε από αυτό (χωρίς να κάνουμε τα πράγματα χειρότερα).

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

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

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

Πώς και πότε συμβαίνουν συγκρούσεις

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

Λάβετε υπόψη, ωστόσο, ότι η "ολοκλήρωση" δεν περιορίζεται μόνο σε "συγχώνευση κλάδων". Μπορεί επίσης να συμβεί κατά την επαναφόρτιση ή τη διαδραστική επαναφορά, κατά την εκτέλεση μιας επιλογής κερασιού ή ενός τράβηγμα, ή ακόμα και κατά την επανεγκατάσταση ενός Stash.

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

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

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

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

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

Υπάρχουν κάποιες άλλες, παρόμοιες καταστάσεις - για παράδειγμα όταν ένα αρχείο τροποποιήθηκε σε έναν κλάδο και διαγράφηκε σε άλλο - αλλά είναι λίγο λιγότερο συνηθισμένο.

Το GUI επιφάνειας εργασίας "Tower" Git , για παράδειγμα, έχει έναν ωραίο τρόπο οπτικοποίησης αυτών των καταστάσεων:

Πώς να ξέρετε πότε συνέβη μια σύγκρουση

Μην ανησυχείτε: Το Git θα σας πει πολύ ξεκάθαρα όταν συνέβη μια σύγκρουση. ?  

Πρώτον, θα σας ενημερώσει αμέσως για την κατάσταση , για παράδειγμα όταν μια συγχώνευση ή επαναφορά αποτύχει λόγω διένεξης:

$ git merge develop Auto-merging index.html CONFLICT (content): Merge conflict in index.html CONFLICT (modify/delete): error.html deleted in HEAD and modified in develop. Version develop of error.html left in tree. Automatic merge failed; fix conflicts and then commit the result.

Όπως μπορείτε να δείτε από το παραπάνω παράδειγμα, όταν προσπάθησα να πραγματοποιήσω μια συγχώνευση, δημιούργησα μια σύγκρουση συγχώνευσης - και ο Git επικοινωνεί το πρόβλημα πολύ καθαρά και γρήγορα:

  • Παρουσιάστηκε διένεξη στο αρχείο "index.html".
  • Παρουσιάστηκε άλλη διένεξη στο αρχείο "error.html".
  • Και τέλος, λόγω των συγκρούσεων, η λειτουργία συγχώνευσης απέτυχε.

Αυτές είναι οι καταστάσεις όπου πρέπει να σκάψουμε τον κώδικα και να δούμε τι πρέπει να γίνει.

Στην απίθανη περίπτωση που έχετε παραβλέψει αυτά τα προειδοποιητικά μηνύματα όταν συνέβη η διένεξη, το Git σας ενημερώνει επιπλέον όποτε εκτελείτε git status:

$ git status On branch main You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Unmerged paths: (use "git add/rm ..." as appropriate to mark resolution) deleted by us: error.html both modified: index.html

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

Πώς να αναιρέσετε μια σύγκρουση στο Git και να ξεκινήσετε ξανά

Οι συγκρούσεις συγχώνευσης έρχονται με έναν ορισμένο αέρα επείγοντος. Και δικαίως: πρέπει να τα αντιμετωπίσετε προτού συνεχίσετε τη δουλειά σας.

Ωστόσο, παρόλο που η παράβλεψή τους δεν είναι επιλογή, η "αντιμετώπιση αντιπαραθέσεων συγχώνευσης" δεν σημαίνει απαραίτητα ότι πρέπει να τις επιλύσετε. Είναι επίσης δυνατή η αναίρεση τους!

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

Σε αυτές τις καταστάσεις, είναι υπέροχο να έχετε κατά νου ότι μπορείτε πάντα να ξεκινήσετε από την αρχή και να επιστρέψετε σε καθαρή κατάσταση προτού συμβεί ακόμη και η σύγκρουση.

Για το σκοπό αυτό, οι περισσότερες εντολές συνοδεύονται από μια --abortεπιλογή, για παράδειγμα git merge --abortκαι git rebase --abort:

$ git merge --abort $ git status On branch main nothing to commit, working tree clean

Αυτό θα σας δώσει την αυτοπεποίθηση ότι πραγματικά δεν μπορείτε να χάσετε. Μπορείτε πάντα να ματαιώσετε, να επιστρέψετε σε καθαρή κατάσταση και να ξεκινήσετε ξανά.

Τι συγκρούσεις φαίνονται πραγματικά στο Git

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

Για παράδειγμα, ας δούμε τα περιεχόμενα του αρχείου (index.html) (που βρίσκεται σε διένεξη) σε έναν επεξεργαστή:

Git was kind enough to mark the problem area in the file, enclosing it in <<<<<<< HEAD and >>>>>>> [other/branch/name]. The content that comes after the first marker originates from our current working branch. Finally, a line with ======= characters separates the two conflicting changes.

How to Solve a Conflict in Git

Our job as developers now is to clean up these lines: after we're finished, the file has to look exactly as we want it to look.

It might be necessary to talk to the teammate who wrote the "other" changes and decide which code is actually correct. Maybe it's ours, maybe it's theirs - or maybe a mixture between the two.

This process - cleaning up the file and making sure it contains what we actually want - doesn't have to involve any magic. You can do this simply by opening your text editor or IDE and starting to making your changes.

Often, however, you'll find that this is not the most efficient way. That's when dedicated tools can save time and effort:

  • Git GUI Tools: Some of the graphical user interfaces for Git can be helpful when solving conflicts. The Tower Git GUI, for example, offers a dedicated "Conflict Wizard" that helps visualize and solve the situation:
  • Dedicated Merge Tools: For more complicated conflicts, it can be great to have a dedicated "Diff & Merge Tool" at hand. You can configure your tool of choice using the "git config" command. (Consult your tool's documentation for detailed instructions.) Then, in case of a conflict, you can invoke it by simply typing git mergetool. As an example, here's a screenshot of "Kaleidoscope" on macOS:

After cleaning up the file - either manually or in a Git GUI or Merge Tool - we have to commit this like any other change:

  • By using git add on the (previously) conflicted file, we inform Git that the conflict has been solved.
  • When all conflicts have been solved and added to the Staging Area, you need to complete the resolution by creating a regular commit.

How to Become More Confident and Productive

Many years ago, when I started using version control, merge conflicts regularly freaked me out: I was afraid that, finally, I had managed to break things for good. ?

Only when I took the time to truly understand what was going on under the hood was I able to deal with conflicts confidently and efficiently.

The same was true, for example, when dealing with mistakes: only once I learned how to undo mistakes with Git was I able to become more confident and productive in my work.

I highly recommend taking a look at the free "First Aid Kit for Git", a collection of short videos about how to undo and recover from mistakes with Git.

Have fun becoming a better programmer!

About the Author

Ο Tobias Günther είναι ο Διευθύνων Σύμβουλος της Tower, του δημοφιλούς πελάτη Git desktop που βοηθά περισσότερους από 100.000 προγραμματιστές σε όλο τον κόσμο να είναι πιο παραγωγικοί με το Git.