- vừa được xem lúc

"Toát mồ hôi" với Git? Những "pha cứu thua" kinh điển với Git

0 0 2

Người đăng: Jimmy Nguyễn

Theo Viblo Asia

Xin chào anh em độc giả của Trà đá công nghệ, lại là tôi - Jim đây!

Sau một thời gian nghỉ ngơi thì hôm nay tôi đã quay trở lại và cùng anh em chém gió về một chủ đề mà chắc hẳn anh em dev nào cũng phải biết khi tham gia vào phát triển phần mềm, đó là Git. Git là một công cụ không thể thiếu cho mọi dự án phát triển phần mềm, nhưng tôi biết không ít anh em lập trình viên, đặc biệt là người mới, đôi khi cảm thấy "toát mồ hôi" khi gặp phải những tình huống oái oăm như commit nhầm, lỡ tay xóa code, hay xung đột với teammate của mình.

Đừng lo lắng, đó là chuyện thường ngày với bất kỳ ai khi mới bắt đầu. Bài viết này là cuốn bí kíp nhỏ tôi đúc kết, tổng hợp những tình huống thường gặp nhất khi làm việc với Git và cách xử lý chúng một cách chuyên nghiệp, dễ hiểu. Với những ví dụ rõ ràng và thực tế, tôi hy vọng anh em sẽ không còn lo sợ mỗi khi Git báo lỗi nữa, mà thay vào đó là sự tự tin để thuần phục công cụ đắc lực này.

Chúng ta cùng bắt đầu nhé!

1. Làm Chủ Git Commit: Sửa Sai Như Thế Nào?

Commit là hành động lưu lại một "ảnh chụp" (snapshot) các thay đổi của bạn vào lịch sử dự án. Tuy nhiên, đôi khi chúng ta lại có những cú trượt tay ở bước quan trọng này.

1.1. Sai Message Commit?

Tình huống: Anh em vừa gõ git commit -m "Add new featr" và nhận ra mình gõ sai "feature", hoặc message commit quá chung chung, không diễn tả đúng những gì mình đã làm.

Giải pháp thần tốc: git commit --amend chính là vị cứu tinh. Lệnh này cho phép bạn sửa lại commit gần nhất.

  • Cách 1: Sửa message trong trình soạn thảo mặc định

    git commit --amend
    

    Sau khi chạy lệnh này, Git sẽ mở một trình soạn thảo văn bản. Anh em chỉ cần sửa lại message cho đúng, sau đó lưu và đóng trình soạn thảo.

  • Cách 2: Sửa message trực tiếp trên command line

    git commit --amend -m "Add new feature"
    

    Cách này nhanh gọn hơn nếu anh em chỉ muốn sửa nhanh message.

Lưu ý vàng: git commit --amend thực chất tạo ra một commit mới thay thế cho commit cũ, tức là nó đang "viết lại lịch sử". Vì vậy, anh em chỉ nên sử dụng cho những commit còn ở local và chưa được đẩy (push) lên remote repository. Nếu commit đó đã được chia sẻ, việc amend và push --force sẽ làm thay đổi lịch sử chung, gây ra rắc rối lớn cho các thành viên khác. Hãy chú ý nhé!

1.2. Commit Đi Lạc Nhánh, Phải Làm Sao?

Tình huống: Anh em đang hăng say code, quên mất việc kiểm tra nhánh và "bem" thẳng một commit quan trọng vào nhánh master hoặc main thay vì nhánh feature của mình.

Tại sao lại nguy hiểm? Nhánh master/main thường là nhánh ổn định, chứa code đã được kiểm thử. Commit trực tiếp code chưa hoàn thiện vào nhánh này có thể làm gián đoạn quy trình làm việc của cả đội.

Kế hoạch giải cứu commit:

  1. Tạo nhánh mới từ commit đi lạc: Giả sử anh em lỡ commit vào main và commit đó đáng lẽ thuộc về nhánh feature/new-login.

    # Giả sử anh em đang ở nhánh main và vừa lỡ commit
    git branch feature/new-login
    

    Lệnh này sẽ tạo một nhánh mới trỏ đến chính commit anh em vừa thực hiện.

  2. Quay lui nhánh main về trạng thái cũ:

    git checkout main
    git reset --hard HEAD~1
    

    Lệnh git reset --hard HEAD~1 sẽ đưa nhánh main lùi lại một commit và xóa bỏ những thay đổi của commit nhầm đó khỏi working directory trên nhánh main.

  3. Chuyển sang nhánh đúng và tiếp tục:

    git checkout feature/new-login
    

    Lúc này, commit "đi lạc" đã nằm an toàn trên nhánh feature/new-login. Anh em có thể yên tâm tiếp tục công việc.

1.3. Xóa Commit An Toàn: Reset vs. Revert

Tình huống: Anh em commit một tính năng thử nghiệm rồi thấy không ổn, commit chứa lỗi nặng, hoặc chỉ là một commit vu vơ và giờ muốn nó biến mất khỏi lịch sử. Có hai phương pháp là git resetgit revert.

  • Trường hợp 1: Commit CHƯA PUSH (còn ở local) Trong trường hợp này, anh em có thể dùng git reset để viết lại lịch sử một cách an toàn.

    • git reset HEAD~1: (Mặc định) Xóa commit cuối, nhưng giữ lại toàn bộ thay đổi trong working directory dưới dạng unstaged.
    • git reset --soft HEAD~1: Xóa commit cuối, nhưng giữ lại thay đổi trong staging area. Rất tiện để gộp vào một commit mới.
    • git reset --hard HEAD~1: CẨN THẬN! Lệnh này xóa commit cuối và xóa luôn tất cả thay đổi của commit đó. Một khi đã reset, việc lấy lại code sẽ rất khó khăn.
  • Trường hợp 2: Commit ĐÃ PUSH lên remote Nếu commit đã được chia sẻ, TUYỆT ĐỐI KHÔNG DÙNG git reset --hard rồi git push --force. Hành động này sẽ gây ra xung đột cực kỳ khó giải quyết cho cả nhóm.

    Giải pháp an toàn là git revert <commit_hash>. Lệnh git revert không xóa commit cũ. Thay vào đó, nó tạo ra một commit MỚI có tác dụng hoàn toàn ngược lại với commit anh em muốn "xóa". Lịch sử được giữ nguyên vẹn, chỉ có thêm một commit "sửa sai", đảm bảo tính nhất quán và an toàn khi làm việc nhóm.

    # Tìm mã hash của commit cần revert
    git log # Thực hiện revert
    git revert def5678a # Push commit revert này lên như bình thường
    git push origin ten-nhanh-cua-ban
    

Bảng so sánh nhanh:

Tính năng git reset (ở local, chưa push) git revert (đã push, hoặc muốn an toàn)
Mục đích "Xóa" commit khỏi lịch sử, di chuyển con trỏ HEAD. Tạo commit mới để "đảo ngược" tác động của một commit cũ.
Lịch sử Thay đổi (viết lại) lịch sử. Không thay đổi lịch sử cũ, chỉ thêm commit mới. An toàn.
Khi nào dùng Dọn dẹp commit local trước khi push. Hoàn tác commit đã push, sửa lỗi trên nhánh chung một cách an toàn.

1.4. Xử Lý Khủng Hoảng: Khi Lỡ Tay Commit Dữ Liệu Nhạy Cảm

Tình huống: Đây là một trong những lỗi chí mạng: vô tình commit file chứa thông tin nhạy cảm như API keys, mật khẩu, file .env vào repository. Nếu repository là public, thông tin này sẽ bị phơi bày cho cả thế giới.

  • Phòng bệnh hơn chữa bệnh:

    • Sử dụng .gitignore: Đây là lá chắn quan trọng nhất. Tạo một file tên là .gitignore ở thư mục gốc, liệt kê các file, thư mục mà anh em muốn Git bỏ qua.
    • Sử dụng biến môi trường: Thay vì viết thẳng thông tin nhạy cảm vào code, hãy lưu chúng vào biến môi trường.
  • Chữa bệnh (nếu đã lỡ commit):

    • Trường hợp 1: Commit CHƯA PUSH: Nếu đó là commit gần nhất, anh em có thể dùng git reset HEAD~1, sau đó thêm file nhạy cảm vào .gitignore và commit lại.
    • Trường hợp 2: Commit ĐÃ PUSH lên public remote: Đây là tình huống rất nghiêm trọng.
      1. HÀNH ĐỘNG NGAY LẬP TỨC: THU HỒI hoặc THAY ĐỔI API key, mật khẩu, hoặc bất kỳ thông tin nào đã bị lộ. Coi như nó đã bị xâm phạm.
      2. Dọn dẹp: Sau đó, anh em có thể cố gắng xóa file đó khỏi toàn bộ lịch sử Git bằng các công cụ như git filter-repo. Tuy nhiên, đây là một quá trình phức tạp. Với người mới, tôi khuyên hãy tập trung vào việc ngăn chặn thiệt hại (thu hồi key) và học cách phòng tránh cho những lần sau.

2. Nghệ Thuật Quản Lý Nhánh (Branches)

Nhánh (branch) là một trong những sức mạnh lớn nhất của Git, cho phép làm việc song song mà không ảnh hưởng đến dòng code chính.

2.1. Đổi Tên Nhánh Chuyên Nghiệp: Local và Remote

Tình huống: Anh em gõ nhầm tên nhánh, hoặc tên nhánh không còn phản ánh đúng mục đích của nó nữa.

  • Đổi tên nhánh local:

    # Nếu đang ở trên nhánh muốn đổi tên
    git branch -m <ten-nhanh-moi> # Nếu đang ở nhánh khác
    git branch -m <ten-nhanh-cu> <ten-nhanh-moi>
    
  • Cập nhật tên nhánh trên remote (nếu đã push): Quy trình gồm 3 bước:

    1. Đổi tên nhánh local như trên.
    2. Xóa nhánh cũ trên remote:
      git push origin --delete <ten-nhanh-cu-tren-remote>
      
    3. Push nhánh mới lên remote và thiết lập theo dõi:
      git push origin -u <ten-nhanh-moi>
      

2.2. Tạm Dừng Công Việc Với Git Stash

Tình huống: Anh em đang code dở một tính năng, các file còn lộn xộn, chưa sẵn sàng để commit. Bỗng có một bug khẩn cấp cần sửa ngay trên nhánh khác.

Bảo bối git stash: Lệnh này cho phép anh em "tạm cất" những thay đổi chưa commit vào một ngăn chứa đặc biệt, giúp working directory trở lại trạng thái sạch sẽ để có thể thoải mái chuyển nhánh.

  • Lưu thay đổi vào stash:
    # Cất thay đổi
    git stash # Cất thay đổi kèm theo message mô tả
    git stash push -m "Dang do tinh nang UI"
    
  • Lấy lại thay đổi:
    • git stash list: Xem danh sách các stash đã lưu.
    • git stash pop: Áp dụng stash gần nhất và xóa nó khỏi danh sách.
    • git stash apply: Tương tự pop nhưng không xóa stash khỏi danh sách.

Lưu ý: Mặc định, git stash chỉ lưu các file đã được Git theo dõi. Để cất cả các file mới tạo (untracked files), anh em dùng git stash -u.

3. Đối Mặt Merge Conflict

Merge conflict (xung đột khi hợp nhất) là một phần rất bình thường trong quy trình làm việc với Git, xảy ra khi Git không thể tự động quyết định nên giữ phiên bản code nào khi hợp nhất hai nhánh.

3.1. Merge Conflict Là Gì?

Conflict xảy ra khi anh em cố gắng hợp nhất hai nhánh mà cả hai đều có những thay đổi trên cùng một phần của cùng một file. Git sẽ dừng quá trình merge lại và yêu cầu anh em can thiệp thủ công.

Khi mở file bị conflict, anh em sẽ thấy các dấu hiệu:

<<<<<<< HEAD
<p>Đây là phiên bản trên nhánh hiện tại của anh em.</p>
=======
<p>Đây là phiên bản từ nhánh đang được merge vào.</p>
>>>>>>> ten-nhanh-dang-merge-vao
  • <<<<<<< HEAD: Bắt đầu phần code của nhánh hiện tại ("ours").
  • =======: Phân tách hai phiên bản.
  • >>>>>>> ...: Kết thúc phần code của nhánh đang merge vào ("theirs").

3.2. Giải Quyết Xung Đột Từng Bước

Khi gặp conflict, hãy bình tĩnh thực hiện các bước sau:

  1. Xác định file bị conflict: Chạy git status để xem danh sách các file ở trạng thái "unmerged paths".
  2. Mở file và phân xử: Mở từng file bị conflict và tìm đến các đoạn được đánh dấu.
  3. Quyết định code cuối cùng: Đây là bước quan trọng nhất. Anh em có thể:
    • Giữ code của nhánh hiện tại.
    • Giữ code của nhánh đang merge vào.
    • Kết hợp cả hai.
    • Viết lại logic mới hoàn toàn. Nếu không chắc chắn, hãy thảo luận với đồng đội của mình.
  4. Dọn dẹp dấu hiệu conflict: Sau khi đã quyết định xong, hãy XÓA BỎ tất cả các dòng dấu hiệu đặc biệt của Git (<<<<<<<, =======, >>>>>>>). File cuối cùng chỉ nên chứa code sạch sẽ.
  5. Lưu file và thêm vào staging:
    git add <ten-file-vua-giai-quyet-conflict>
    
  6. Hoàn tất quá trình merge: Khi đã giải quyết hết các file, hãy tạo một commit mới để hoàn tất.
    git commit
    
    Git sẽ tự động tạo một commit message mẫu, anh em có thể giữ nguyên hoặc sửa lại.

3.3. Tăng Tốc Với Công Cụ và Lối Thoát An Toàn

Hầu hết các trình soạn thảo code hiện đại như Visual Studio Code đều có công cụ tích hợp giúp giải quyết conflict trực quan hơn. VS Code sẽ highlight các vùng conflict và cung cấp các lựa chọn nhanh như:

  • Accept Current Change
  • Accept Incoming Change
  • Accept Both Changes

Việc này giúp giảm đáng kể thời gian và sai sót.

Lỡ làm rối tung và muốn làm lại từ đầu?

git merge --abort

Lệnh này sẽ hủy bỏ hoàn toàn quá trình merge, đưa mọi thứ trở lại trạng thái như trước khi conflict xảy ra.

4. Làm Việc Với Remote: Những Bài Học Kinh Nghiệm

Remote repository là trung tâm cộng tác của dự án. Việc tương tác với nó cũng có những "cạm bẫy" riêng.

4.1. Lỗi Kinh Điển: "src refspec ... does not match any"

Lỗi này về cơ bản có nghĩa là Git không tìm thấy nhánh bạn đang cố gắng push trên local repository của mình.

Nguyên nhân phổ biến:

  • Chưa có commit nào: Một nhánh chỉ thực sự tồn tại sau khi có ít nhất một commit. Nếu anh em git init mà chưa git commit, lệnh push sẽ thất bại.
  • Tên nhánh local khác remote (master vs. main): Các nền tảng mới như GitHub mặc định dùng tên main, trong khi Git client cũ có thể mặc định là master. Nếu anh em cố git push -u origin main trong khi nhánh local tên là master, lỗi sẽ xảy ra.

Cách khắc phục:

  1. Kiểm tra tên nhánh local: git branch
  2. Đảm bảo đã có commit: git log. Nếu chưa, hãy git add .git commit -m "Initial commit".
  3. Đổi tên nhánh local cho khớp (khuyến khích):
    # Đổi tên nhánh hiện tại từ master thành main
    git branch -M main
    
    Sau đó push bình thường: git push -u origin main.

4.2. Quy Tắc Vàng: Luôn Pull Trước Khi Push

Tình huống: Anh em git push và nhận được lỗi non-fast-forward.

Lỗi này xảy ra vì lịch sử commit trên remote đã có những thay đổi mới mà trên máy anh em chưa có. Git sẽ từ chối việc push để bảo vệ, tránh làm mất code của người khác.

Cách xử lý êm đẹp:

  1. Cập nhật thay đổi mới nhất từ remote:
    git pull
    
    Lệnh git pull thực chất là sự kết hợp của git fetch (tải thay đổi) và git merge (trộn vào nhánh của bạn).
  2. Giải quyết conflict (nếu có): Quá trình pull có thể gây ra conflict. Hãy giải quyết chúng như đã hướng dẫn ở Phần 3.
  3. Push lại: Sau khi đã pull thành công và giải quyết mọi conflict, nhánh local của anh em đã bắt kịp với remote. Giờ anh em có thể tự tin git push lại.

5. Nâng Cao: Dọn Dẹp Lịch Sử Commit Với Interactive Rebase

Khi phát triển một tính năng, lịch sử commit local của anh em có thể trở nên lộn xộn với các commit nhỏ, vụn vặt. git rebase -i (interactive rebase) là công cụ cực kỳ mạnh mẽ để "tút tát" lại lịch sử này.

CẢNH BÁO CỰC KỲ QUAN TRỌNG

git rebase VIẾT LẠI LỊCH SỬ COMMIT. Anh em CHỈ NÊN SỬ DỤNG rebase đối với những commit CHƯA ĐƯỢC PUSH lên các nhánh đã chia sẻ (shared branch). Đừng bao giờ rebase nhánh main hoặc các nhánh feature chung mà nhiều người đang cùng làm việc. Việc này sẽ gây ra "đại họa" conflict cho cả team.

5.1. Sức Mạnh Của git rebase -i

Lệnh git rebase -i HEAD~N cho phép anh em can thiệp vào N commit gần nhất. Sau khi chạy lệnh, Git sẽ mở một trình soạn thảo liệt kê các commit đó. Anh em có thể thay đổi từ khóa pick ở đầu mỗi dòng thành các lệnh sau:

  • pick (p): Giữ nguyên commit.
  • reword (r): Giữ nguyên code, nhưng cho phép sửa lại commit message.
  • edit (e): Dừng lại ở commit đó để anh em có thể sửa đổi code, sau đó git commit --amendgit rebase --continue.
  • squash (s): Gộp commit này vào commit ở dòng ngay trên nó, và cho phép viết một message tổng hợp mới.
  • fixup (f): Tương tự squash, nhưng bỏ qua hoàn toàn message của commit này. Rất tiện để gộp các commit sửa lỗi nhỏ.
  • drop (d): Xóa bỏ hoàn toàn commit này.

Ngoài ra, anh em cũng có thể thay đổi thứ tự các dòng để sắp xếp lại các commit.

5.2. Case Study: Tút Tát Lịch Sử Commit

Giả sử lịch sử commit của anh em như sau:

i7j8k9l WIP - add avatar upload
e4f5g6h Fix small typo in user model
a1b2c3d Add basic user profile fields

Anh em chạy git rebase -i HEAD~3. Trình soạn thảo mở ra:

pick a1b2c3d Add basic user profile fields
pick e4f5g6h Fix small typo in user model
pick i7j8k9l WIP - add avatar upload

Bây giờ, anh em sửa lại để gộp commit "Fix typo" vào commit "Add fields" và sửa lại commit "WIP":

pick a1b2c3d Add basic user profile fields
fixup e4f5g6h Fix small typo in user model
edit i7j8k9l WIP - add avatar upload

Sau khi lưu và đóng file, Git sẽ gộp commit fixup, sau đó dừng lại ở commit edit để anh em có thể git commit --amend và sửa message thành "Implement avatar upload functionality", cuối cùng là git rebase --continue.

Kết quả, lịch sử commit của anh em sẽ gọn gàng và rõ ràng hơn rất nhiều.

5.3. Khi Nào Nên Sử Dụng Rebase?

  • Trước khi tạo Pull Request (PR): Dọn dẹp các commit nháp để người review dễ theo dõi.
  • Để làm lịch sử commit cá nhân (trên các nhánh chưa chia sẻ) trở nên dễ đọc và có ý nghĩa hơn.

6. Lời Kết: Hành Trình Chinh Phục Git

Vậy là chúng ta đã cùng nhau đi qua những tình huống Git kinh điển. Những "chiêu thức" chính anh em đã học được bao gồm:

  • Sửa lỗi commit nhanh chóng với git commit --amend.
  • Lựa chọn giữa git reset (cho local) và git revert (cho shared history).
  • Tầm quan trọng của .gitignore và cách xử lý khi lỡ commit thông tin nhạy cảm.
  • Sử dụng git stash để linh hoạt chuyển đổi công việc.
  • Các bước tự tin giải quyết merge conflict.
  • Sử dụng git rebase -i một cách cẩn trọng để dọn dẹp lịch sử commit local.

Git có thể ban đầu trông đáng sợ, nhưng nó không hề khó nếu anh em dành thời gian tìm hiểu và thực hành thường xuyên. Mỗi lỗi anh em gặp phải chính là một cơ hội để hiểu sâu hơn về cách Git hoạt động.

Chúc anh em "thuần phục" Git thành công, biến nó thành một trợ thủ đắc lực giúp công việc lập trình của mình trở nên hiệu quả và chuyên nghiệp hơn. Hành trình từ người mới bắt đầu đến chuyên gia, cũng như bất kỳ kỹ năng nào khác, đều cần thời gian và sự kiên trì. Hãy tiếp tục học hỏi và đừng bao giờ ngừng khám phá. Hẹn gặp lại anh em trong những bài viết sau tại Trà đá công nghệ!

Bình luận

Bài viết tương tự

- vừa được xem lúc

Đặt tên commit message sao cho "tình nghĩa anh em chắc chắn bền lâu"????

. Lời mở đầu. .

1 2 1.1k

- vừa được xem lúc

Tập hợp những câu lệnh GIT hữu dụng

Dưới đây là một vài ví dụ về các câu lệnh Git mà tôi thường dùng. git config --global user.name "John Doe". git config --global user.

0 0 76

- vừa được xem lúc

Cấu hình CI/CD với Github (phần 2): Trigger một work flow

Events trigger. Bạn có thể cấu hình cho workflows chạy khi có một sự kiện nào đó xảy ra trên GitHub, theo một lịch có sẵn hoặc cũng có thể là một sự kiện nào đó xảy ra ngoài GitHub.

0 0 91

- vừa được xem lúc

Cấu hình CI/CD với Github (phần 1): Một ít lý thuyết

CI/CD là gì. Về mặt khái niệm là vậy nhưng về mặt triển khai thì CI/CD là quá trình tự động thực hiện các quá trình build, test, release, deploy khi có các trigger như commit/merge code lên một branch định sẵn hoặc có thể là tự động chạy theo một lịch cố định.

0 0 133

- vừa được xem lúc

Giới thiệu về Git LFS

. Git LFS là gì . Git LFS làm điều này bằng cách thay thế các tệp lớn trong repo của bạn bằng một con trỏ nhỏ.

0 0 43

- vừa được xem lúc

Git workflow được Google và Facebook sử dụng có gì hay ho

Với developer thì Git hẳn là công cụ rất quen thuộc và không thể thiếu rồi. Thế nhưng có mấy ai thực sự hiểu được Git.

0 0 95