Cách hiểu và giải quyết xung đột trong Git

Đây rồi, từ mà mọi nhà phát triển đều ghét phải nhìn thấy: xung đột. ? Không có cách nào để giải quyết xung đột hợp nhất không thường xuyên khi làm việc với Git (hoặc các hệ thống kiểm soát phiên bản khác).

Nhưng khi nói chuyện với các nhà phát triển, tôi thường nghe rằng có cảm giác lo lắng hoặc khó chịu xung quanh chủ đề xung đột hợp nhất.

Xử lý xung đột thường vẫn là một nơi bí ẩn, tăm tối: một tình huống mà mọi thứ đang bị phá vỡ một cách tồi tệ và không rõ làm cách nào để thoát khỏi nó (mà không làm cho mọi thứ trở nên tồi tệ hơn).

Mặc dù đúng là xung đột hợp nhất là một phần không thể tránh khỏi trong cuộc sống của nhà phát triển, nhưng sự khó chịu trong những tình huống này là hoàn toàn tùy chọn.

Mục đích của tôi với bài viết này là mang lại sự rõ ràng cho chủ đề này: xung đột thường xảy ra như thế nào và khi nào, chúng thực sự là gì và cách giải quyết - hoặc hoàn tác - chúng.

Khi hiểu đúng những điều này, bạn sẽ có thể giải quyết các xung đột hợp nhất một cách thoải mái và tự tin hơn nhiều. ?

Xung đột xảy ra như thế nào và khi nào

Cái tên đã nói lên điều đó: "xung đột hợp nhất" có thể xảy ra trong quá trình tích hợp các cam kết từ một nguồn khác.

Tuy nhiên, hãy nhớ rằng "tích hợp" không chỉ giới hạn ở "hợp nhất các nhánh". Nó cũng có thể xảy ra khi phục hồi hoặc phục hồi tương tác, khi thực hiện một quả anh đào hoặc một cú kéo, hoặc thậm chí khi áp dụng lại một Stash.

Tất cả các hành động này thực hiện một số loại tích hợp - và đó là khi xung đột hợp nhất có thể xảy ra.

Nhưng tất nhiên, những hành động này không dẫn đến xung đột hợp nhất mọi lúc (cảm ơn Chúa!). Tốt nhất, bạn chỉ nên thấy mình hiếm khi rơi vào những tình huống này. Nhưng chính xác thì xung đột xảy ra khi nào?

Trên thực tế, khả năng hợp nhất của Git là một trong những lợi thế lớn nhất của nó: việc hợp nhất các nhánh hoạt động dễ dàng hầu hết thời gian, vì Git thường có thể tự tìm ra mọi thứ.

Nhưng có những tình huống mà những thay đổi trái ngược nhau được thực hiện - và nơi công nghệ không thể quyết định điều gì đúng hay sai. Những tình huống này chỉ đơn giản là yêu cầu quyết định của con người.

Điều cổ điển thực sự là khi cùng một dòng mã được thay đổi trong hai lần cam kết, trên hai nhánh khác nhau. Git không có cách nào để biết bạn thích thay đổi nào! ?

Có một số tình huống tương tự khác - ví dụ như khi một tệp được sửa đổi trong một nhánh và bị xóa trong một nhánh khác - nhưng chúng ít phổ biến hơn một chút.

Các "Tower" Git máy tính để bàn giao diện đồ họa , ví dụ, có một cách tốt đẹp của hình dung những loại tình huống:

Làm thế nào để biết khi xung đột xảy ra

Đừng lo lắng: Git sẽ cho bạn biết rất rõ ràng khi xung đột đã xảy ra. ?  

Đầu tiên, nó sẽ cho bạn biết ngay lập tức trong tình huống , chẳng hạn như khi hợp nhất hoặc rebase không thành công do xung đột:

$ 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.

Như bạn có thể thấy từ ví dụ trên, khi tôi cố gắng thực hiện hợp nhất, tôi đã tạo ra xung đột hợp nhất - và Git thông báo vấn đề rất rõ ràng và kịp thời:

  • Đã xảy ra xung đột trong tệp "index.html".
  • Một xung đột khác trong tệp "error.html" đã xảy ra.
  • Và cuối cùng, do xung đột, hoạt động hợp nhất không thành công.

Đây là những tình huống mà chúng ta phải đào sâu vào mã và xem những gì phải làm.

Trong trường hợp không chắc rằng bạn đã bỏ qua các thông báo cảnh báo này khi xung đột xảy ra, Git cũng sẽ thông báo cho bạn bất cứ khi nào bạn chạy 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

Nói cách khác: đừng lo lắng về việc không nhận thấy xung đột hợp nhất. Git đảm bảo rằng bạn không thể bỏ qua chúng.

Cách hoàn tác xung đột trong Git và bắt đầu lại

Xung đột hợp nhất đi kèm với một không khí khẩn trương nhất định. Và đúng như vậy: bạn sẽ phải giải quyết chúng trước khi bạn có thể tiếp tục công việc của mình.

Nhưng mặc dù bỏ qua chúng không phải là một lựa chọn, "giải quyết xung đột hợp nhất" không nhất thiết có nghĩa là bạn phải giải quyết chúng. Hoàn tác chúng cũng có thể!

Điều này có thể đáng để lặp lại: bạn luôn có tùy chọn để hoàn tác xung đột hợp nhất và trở lại trạng thái trước đó. Điều này đúng ngay cả khi bạn đã bắt đầu giải quyết các tệp xung đột và thấy mình đang đi vào ngõ cụt.

Trong những tình huống này, bạn nên nhớ rằng bạn luôn có thể bắt đầu lại và trở về trạng thái trong sạch trước khi xung đột xảy ra.

For this purpose, most commands come with an --abort option, for example git merge --abort and git rebase --abort:

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

This should give you the confidence that you really cannot mess up. You can always abort, return to a clean state, and start over.

What Conflicts Really Look Like in Git

Now, safe in the knowledge that nothing can break, let's see what a conflict really looks like under the hood. This will demystify those little buggers and, at the same time, help you lose respect for them and gain confidence in yourself.

As an example, let's look at the contents of the (currently conflicted) "index.html" file in an editor:

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 là Giám đốc điều hành của Tower, ứng dụng khách phổ biến trên máy tính để bàn Git giúp hơn 100.000 nhà phát triển trên khắp thế giới làm việc hiệu quả hơn với Git.