วิธีทำความเข้าใจและแก้ไขความขัดแย้งใน Git

มีคำที่นักพัฒนาทุกคนเกลียดที่จะเห็น: ความขัดแย้ง 😱ไม่มีทางหลีกเลี่ยงความขัดแย้งในการผสานเป็นครั้งคราวเมื่อทำงานกับ Git (หรือระบบควบคุมเวอร์ชันอื่น ๆ )

แต่เมื่อพูดคุยกับนักพัฒนาฉันมักจะได้ยินว่ามีความกังวลหรือไม่สบายใจเกี่ยวกับหัวข้อของการผสานความขัดแย้ง

การจัดการความขัดแย้งมักจะยังคงเป็นสถานที่ที่มืดมนและลึกลับ: สถานการณ์ที่สิ่งต่างๆพังทลายอย่างรุนแรงและไม่มีความชัดเจนว่าจะออกไปจากมันได้อย่างไร (โดยไม่ทำให้สิ่งต่างๆแย่ลง)

แม้ว่าจะเป็นความจริงที่การผสานความขัดแย้งเป็นส่วนหนึ่งของชีวิตของนักพัฒนาอย่างหลีกเลี่ยงไม่ได้ แต่ความรู้สึกไม่สบายในสถานการณ์เหล่านี้เป็นทางเลือกอย่างเต็มที่

ความตั้งใจของฉันในบทความนี้คือการทำให้เกิดความชัดเจนในหัวข้อนี้: โดยทั่วไปแล้วความขัดแย้งจะเกิดขึ้นได้อย่างไรและเมื่อใดสิ่งที่เป็นจริงและวิธีการแก้ไขหรือเลิกทำ

เมื่อคุณเข้าใจสิ่งเหล่านี้อย่างถูกต้องคุณจะสามารถจัดการกับการผสานความขัดแย้งด้วยวิธีที่ผ่อนคลายและมั่นใจมากขึ้น 😍

ความขัดแย้งเกิดขึ้นอย่างไรและเมื่อใด

ชื่อนี้บอกอยู่แล้ว: "ผสานความขัดแย้ง" สามารถเกิดขึ้นได้ในกระบวนการรวมคอมมิตจากแหล่งอื่น

อย่างไรก็ตามโปรดทราบว่า "การรวม" ไม่ได้ จำกัด เฉพาะ "การรวมสาขา" เท่านั้น นอกจากนี้ยังสามารถเกิดขึ้นได้เมื่อทำการ rebasing หรือการ rebasing แบบโต้ตอบเมื่อทำการเลือกเชอร์รี่หรือการดึงหรือแม้กระทั่งเมื่อนำ Stash กลับมาใช้ใหม่

การดำเนินการทั้งหมดนี้เป็นการผสานรวมบางอย่างและนั่นคือสิ่งที่ทำให้เกิดความขัดแย้งในการผสานได้

แต่แน่นอนว่าการกระทำเหล่านี้ไม่ได้ส่งผลให้เกิดความขัดแย้งทุกครั้ง (ขอบคุณพระเจ้า!) ตามหลักการแล้วคุณควรพบว่าตัวเองอยู่ในสถานการณ์เหล่านี้เพียงไม่บ่อย แต่ความขัดแย้งเกิดขึ้นเมื่อใด?

จริงๆแล้วความสามารถในการผสานรวมของ Git เป็นข้อดีอย่างหนึ่ง: การรวมสาขาทำงานได้อย่างง่ายดายเกือบตลอดเวลาเพราะโดยปกติ Git จะสามารถคิดสิ่งต่างๆได้ด้วยตัวเอง

แต่มีบางสถานการณ์ที่เกิดการเปลี่ยนแปลงที่ขัดแย้งกันและในกรณีที่เทคโนโลยีไม่สามารถตัดสินได้ว่าอะไรถูกหรือผิด สถานการณ์เหล่านี้ต้องการการตัดสินใจจากมนุษย์

คลาสสิกที่แท้จริงคือเมื่อมีการเปลี่ยนแปลงบรรทัดของโค้ดเดียวกันในสองคอมมิตในสองสาขาที่แตกต่างกัน Git ไม่มีทางรู้ว่าคุณชอบการเปลี่ยนแปลงใด! 🤔

มีสถานการณ์อื่นที่คล้ายคลึงกันตัวอย่างเช่นเมื่อไฟล์ถูกแก้ไขในสาขาหนึ่งและลบไปในอีกสาขาหนึ่ง - แต่จะพบได้น้อยกว่าเล็กน้อย

"หอคอย" แจก GUI เดสก์ทอป , เช่นมีวิธีที่ดีในการแสดงภาพสถานการณ์เหล่านี้:

จะรู้ได้อย่างไรว่าเมื่อมีความขัดแย้งเกิดขึ้น

ไม่ต้องกังวล: 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 มีลักษณะอย่างไร

ตอนนี้ปลอดภัยในความรู้ที่ไม่มีอะไรสามารถทำลายได้เรามาดูว่าความขัดแย้งที่เกิดขึ้นจริงภายใต้ประทุนนั้นเป็นอย่างไร สิ่งนี้จะทำให้เข้าใจผิดว่าเป็นพวกโรคจิตและในขณะเดียวกันก็ช่วยให้คุณไม่เคารพพวกเขาและมั่นใจในตัวเอง

ตัวอย่างเช่นเรามาดูเนื้อหาของไฟล์ "index.html" (ที่ขัดแย้งกันในปัจจุบัน) ในโปรแกรมแก้ไข:

Git นั้นดีพอที่จะทำเครื่องหมายพื้นที่ปัญหาในไฟล์โดยใส่ไว้ใน<<<<<<< HEADและ>>>>>>> [other/branch/name]. เนื้อหาที่อยู่หลังเครื่องหมายแรกมาจากสาขาการทำงานปัจจุบันของเรา ในที่สุดบรรทัดที่มี=======อักขระจะแยกการเปลี่ยนแปลงทั้งสองที่ขัดแย้งกัน

วิธีแก้ความขัดแย้งใน Git

งานของเราในฐานะนักพัฒนาในตอนนี้คือการล้างบรรทัดเหล่านี้: หลังจากที่เราทำเสร็จแล้วไฟล์จะต้องมีลักษณะตรงตามที่เราต้องการ

อาจจำเป็นต้องพูดคุยกับเพื่อนร่วมทีมที่เขียนการเปลี่ยนแปลง "อื่น ๆ " และตัดสินใจว่ารหัสใดถูกต้องจริง อาจจะเป็นของเราหรืออาจจะเป็นของพวกเขา - หรืออาจจะเป็นส่วนผสมระหว่างทั้งสอง

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 is the CEO of Tower, the popular Git desktop client that helps more than 100,000 developers around the world to be more productive with Git.