phỏng vấn dev #6, câu hỏi phỏng vấn git

Phỏng vấn dev truyền kỳ #6: Git – Bạn sửa một commit đã đẩy lên remote như thế nào?

Chào các bạn, Lucas đây!

Trong thế giới lập trình, Git là một công cụ quản lý mã nguồn thiết yếu và là “người bạn đồng hành” không thể thiếu của mỗi lập trình viên (nếu bạn muốn tìm hiểu thêm về nghề lập trình viên nói chung). Việc thành thạo Git không chỉ giúp bạn làm việc hiệu quả mà còn thể hiện sự chuyên nghiệp. Hôm nay, Lucas sẽ chia sẻ một câu hỏi phỏng vấn Git mà Lucas thường dùng để kiểm tra xem cách sử dụng Git và kiểm soát công cụ này của các bạn ở mức độ nào.

Lucas thường đặt ra các câu hỏi mở và tình huống thực tế để kiểm tra khả năng vận dụng các cụm lệnh phù hợp của ứng viên, thay vì chỉ hỏi về một lệnh Git cụ thể. Câu hỏi này cũng là một ví dụ điển hình, đòi hỏi không chỉ việc nhớ lệnh Git mà còn cả tư duy và sự hiểu biết sâu sắc về cách kiểm soát lịch sử thay đổi của mã nguồn.

Mức độ hiểu biết về Git qua câu hỏi sửa commit

Khi Lucas hỏi “Bạn sửa commit đã đẩy lên remote như thế nào?”, câu trả lời của các bạn sẽ giúp Lucas hình dung được kinh nghiệm thực tế của bạn với Git.

Mức độ 1: Chưa từng đối mặt với tình huống phức tạp

Nếu bạn chưa từng phải xử lý tình huống này, điều đó cho thấy bạn có thể chưa làm việc với các dự án lớn, hoặc chưa gặp phải các trường hợp cần “động chạm” vào lịch sử commit đã được chia sẻ. Đây là điều hoàn toàn bình thường với các bạn mới bắt đầu, nhưng cũng là một dấu hiệu để Lucas biết rằng bạn cần được hướng dẫn thêm về các tình huống thực tế khi quản lý mã nguồn Git. Nếu bạn chưa giỏi Git, hãy tìm hiểu thêm ở các blogger nhiệt tình như series Git cơ bản của Thạch Phạm nhé.

Mức độ 2: Biết cách sửa commit mới nhất đã đẩy lên (Amend)

Đây là mức độ phổ biến hơn một chút. Các bạn ở mức độ này thường biết cách sử dụng Git để sửa commit gần nhất mà mình vừa đẩy lên remote.

Khái niệm: Khi bạn nhận ra mình vừa đẩy một commit lên remote nhưng lại muốn sửa một lỗi nhỏ, thêm một file, hoặc thay đổi thông điệp commit, bạn có thể dùng git commit --amend để sửa commit đó ở local. Tuy nhiên, vì commit này đã được đẩy lên remote, lịch sử commit của bạn đã thay đổi so với remote. Lúc này, bạn sẽ cần dùng git push --force hoặc git push --force-with-lease để “ghi đè” lịch sử trên remote.

Chuỗi lệnh ví dụ:

# Sửa đổi file hoặc thêm file mới
git add .
git commit --amend --no-edit # Sửa commit gần nhất, giữ nguyên thông điệp cũ
# Hoặc: git commit --amend -m "Thông điệp commit mới"
git push --force-with-lease origin <tên_nhánh_của_bạn>

Lucas thường khuyến khích dùng git push --force-with-lease thay vì git push --force vì nó an toàn hơn. force-with-lease sẽ kiểm tra xem remote branch có thay đổi gì mà bạn chưa biết không trước khi force push, giúp tránh ghi đè lên công việc của người khác. Đây là một phần quan trọng trong cách sử dụng Git hiệu quả.

Mức độ 3: Biết cách sửa mọi commit bằng Rebase và Force Push

Đây là mức độ mà Lucas đánh giá cao nhất, cho thấy bạn có sự kiểm soát sâu sắc về lịch sử commit Git. Ở mức độ này, bạn có thể sửa bất kỳ commit nào trong lịch sử (không chỉ commit gần nhất) bằng cách “lật ngược” lịch sử, sửa đổi, sau đó “viết lại” lịch sử và đẩy lên remote.

Khái niệm: Để sửa một commit không phải là commit gần nhất, bạn sẽ cần dùng git rebase -i (interactive rebase). Lệnh này cho phép bạn tương tác với lịch sử commit, ví dụ như:

  • reword: Sửa thông điệp commit.
  • edit: Sửa nội dung của commit.
  • squash: Gộp nhiều commit lại thành một.
  • drop: Xóa bỏ commit.

Sau khi sửa đổi lịch sử commit ở local, bạn lại cần git push --force-with-lease để cập nhật lên remote.

Chuỗi lệnh ví dụ:

# Giả sử bạn muốn sửa commit thứ 3 từ dưới lên (HEAD~3)
git rebase -i HEAD~3

# Một editor sẽ hiện ra với danh sách các commit.
# Thay "pick" bằng "edit" (hoặc "e") ở commit bạn muốn sửa.
# Lưu và thoát editor.

# Sau khi rebase dừng lại ở commit cần sửa:
# Sửa đổi file hoặc thêm file mới
git add .
git commit --amend --no-edit # Sửa commit hiện tại (đã được "edit" trong rebase)

# Tiếp tục rebase cho đến khi hoàn tất
git rebase --continue

# Cuối cùng, đẩy lịch sử đã thay đổi lên remote
git push --force-with-lease origin <tên_nhánh_của_bạn>

Việc sửa lịch sử commit Git đã được đẩy lên remote là một hành động mạnh mẽ và cần thận trọng, đặc biệt khi làm việc nhóm.

With great power comes great responsibility.

Sức mạnh lớn đi kèm trách nhiệm cao.

Là một câu nói rất đúng trong trường hợp này. Chỉ nên làm khi bạn chắc chắn và đã trao đổi với đồng đội.

Các câu hỏi liên quan đến Git khác

Ngoài câu hỏi trên, Lucas cũng thường hỏi thêm để đánh giá toàn diện kiến thức Git của ứng viên trong các buổi phỏng vấn dev:

Git fetch và Git pull khác nhau như thế nào?

Đây là câu hỏi phỏng vấn Git cơ bản nhưng Lucas rất hay hỏi để kiểm tra sự hiểu biết của ứng viên về cách sử dụng Git và cách nó tương tác với remote repository.

  • git fetch: Tải các thay đổi từ remote về local repository nhưng không tự động hợp nhất (merge) vào nhánh làm việc hiện tại. Nó chỉ cập nhật các remote-tracking branches.
  • git pull: Là sự kết hợp của git fetchgit merge. Nó tải các thay đổi từ remote và tự động hợp nhất vào nhánh làm việc hiện tại của bạn.

Git merge và Git rebase khác nhau như thế nào?

Lucas thường dùng câu hỏi này để kiểm tra xem ứng viên hiểu rõ về cách sử dụng Git và cách chúng xử lý lịch sử commit như thế nào.

  • git merge: Tạo ra một commit mới (merge commit) để hợp nhất lịch sử của hai nhánh, giữ nguyên lịch sử ban đầu.
  • git rebase: “Viết lại” lịch sử commit bằng cách di chuyển hoặc kết hợp một chuỗi commit để chúng xuất hiện như thể được thực hiện trên một nhánh khác, tạo ra một lịch sử tuyến tính (linear history) và sạch sẽ hơn.

Khi nào nên dùng Git reset và Git revert?

Lucas thường hỏi về git resetgit revert để đánh giá cách sử dụng Git của ứng viên trong việc hoàn tác thay đổi một cách an toàn và hiệu quả.

  • git reset: Di chuyển con trỏ HEAD và/hoặc nhánh về một commit cũ hơn, thường được dùng để loại bỏ các commit khỏi lịch sử commit (local). Nó có thể làm mất dữ liệu nếu không cẩn thận.
  • git revert: Tạo ra một commit mới để “hoàn tác” các thay đổi của một commit cũ, giữ nguyên lịch sử commit ban đầu. Thường được dùng để hoàn tác các thay đổi đã được đẩy lên remote một cách an toàn.

Bạn dùng Git stash khi nào?

Đây là một câu hỏi thực tế mà Lucas thường hỏi để kiểm tra xem ứng viên có biết cách sử dụng Git để quản lý công việc dở dang một cách linh hoạt hay không.

git stash là một lệnh rất tiện lợi khi bạn đang làm việc dở dang trên một nhánh nhưng cần chuyển sang làm việc khác mà không muốn commit tạm thời. Nó giúp lưu trữ các thay đổi chưa commit vào một “ngăn chứa” tạm thời để bạn có thể quay lại sau. Để tìm hiểu sâu hơn về các lệnh và tham số của Git, bạn có thể tham khảo tài liệu chính thức của Git.

Muốn giỏi Git thì phải tiếp cận nó thế nào?

Lucas biết rằng, với rất nhiều lệnh và tham số phức tạp, việc học Git có thể khiến nhiều bạn cảm thấy “tẩu hỏa nhập ma” nếu chỉ cố gắng ghi nhớ từng câu lệnh một cách máy móc. Đừng lo lắng, đó là cảm giác rất bình thường! Cách sử dụng Git hiệu quả không nằm ở việc bạn nhớ được bao nhiêu lệnh, mà ở việc bạn hiểu được nguyên lý của nó. Nếu bạn đang ở buổi đầu học tập ngành công nghệ thông tin, hãy tham khảo thêm về lập trình cơ bản và bộ khung kiến thức căn bản để có nền tảng vững chắc nhé.

Thay vì thế, hãy tiếp cận Git bằng cách hiểu các vấn đề cốt lõi:

Hiểu rõ các nguyên lý cốt lõi của Git

Đây là hình ảnh minh họa Lucas mượn từ trang https://stackoverflow.com/questions/57547095/where-does-git-merge-fit-in-this-diagram. Hình ảnh này khá là cơ bản để bạn hình dung ra vai trò của các khu vực làm việc của Git.

Mô hình Git, Các thành phần Git
Các thành phần của Git và luồng trao đổi dữ liệu khi thực hiện các lệnh cơ bản của Git.
  • Phân biệt rõ ràng Remote, Local, Staging và Working Directory: Đây là bốn khu vực làm việc chính trong Git.
    • Working Directory (Thư mục làm việc): Nơi chứa các file mà bạn đang chỉnh sửa trực tiếp.
    • Staging Area (Khu vực tổ chức/Index): Là một lớp trung gian giữa Working Directory và Local Repository. Đây là nơi bạn chọn lọc các thay đổi muốn đưa vào commit tiếp theo bằng lệnh git add.
    • Local Repository (Kho chứa cục bộ):: Nơi Git lưu trữ lịch sử các phiên bản của dự án trên máy tính của bạn (thư mục .git).
    • Remote Repository (Kho chứa từ xa): Nơi dự án được lưu trữ trên một máy chủ từ xa (ví dụ GitHub, GitLab), là nơi để các thành viên trong nhóm chia sẻ mã nguồn. Hiểu rõ vai trò và mối quan hệ giữa ba khu vực này là nền tảng để bạn quản lý mã nguồn Git hiệu quả.
  • Hiểu rõ Git quản lý thay đổi mã nguồn bằng lịch sử commits: Git không lưu trữ các phiên bản đầy đủ của toàn bộ dự án mỗi khi bạn commit. Thay vào đó, nó lưu trữ các “snapshot” (ảnh chụp nhanh) của dự án tại mỗi thời điểm commit. Mỗi commit là một “bản ghi” về những thay đổi đã diễn ra, và chúng được liên kết với nhau tạo thành một lịch sử commit Git hoàn chỉnh. Việc hiểu rằng Git là một đồ thị các commit giúp bạn dễ dàng hình dung và thao tác với lịch sử.
  • Git là một hệ thống phân tán: Mỗi lập trình viên có một bản sao đầy đủ của kho chứa (repository) trên máy tính của mình. Điều này có nghĩa là bạn có thể làm việc offline và chỉ cần kết nối mạng khi muốn đồng bộ với remote.
  • Nguyên tắc bất biến của commit: Một khi commit đã được tạo, về mặt kỹ thuật, nó không thay đổi. Khi bạn “sửa” một commit, thực chất là bạn đang tạo ra một commit mới với nội dung mong muốn, và Git sẽ điều chỉnh lịch sử để trỏ đến commit mới này.

Học các “best practices” cho từng tình huống cụ thể

Đừng học lệnh Git một cách máy móc, mà hãy đặt ra từng tình huống cụ thể có thể gặp trong thực tế. Sau khi nắm vững những nguyên lý cốt lõi ở trên, việc học các lệnh Git sẽ trở nên dễ dàng hơn rất nhiều. Lúc đó, hãy tìm hiểu và học các “best practices” (thực hành tốt nhất) cho các tình huống cụ thể:

  • Muốn pick một commit vào nhánh khác thì làm thế nào? (Sử dụng git cherry-pick)
  • Muốn gom một nhóm các commit lại cho đỡ “rác” lịch sử thì làm thế nào? (Sử dụng git rebase -i với tùy chọn squash)
  • Muốn hoàn tác một thay đổi đã được đẩy lên remote một cách an toàn mà không làm mất lịch sử? (Sử dụng git revert)

Việc học Git theo cách này sẽ giúp bạn không chỉ biết cách sử dụng Git mà còn hiểu “tại sao” và “khi nào” nên sử dụng các lệnh tương ứng, từ đó trở thành một lập trình viên chuyên nghiệp hơn trong việc quản lý mã nguồn Git.

Sau khi đã nắm được cách tốt nhất cho một tình huống, bạn sẽ dễ dàng sáng tạo, phối kết hợp các tham số khác nhau để việc sử dụng Git của bạn trở nên mượt mà, nuột nà hơn. Điều này cũng liên quan đến tố chất sáng tạo của một lập trình viên giỏi.

Kết luận: Git là một kỹ năng sống còn

Việc thành thạo Git không chỉ là biết các lệnh mà còn là hiểu rõ cách sử dụng Git và khi nào nên sử dụng chúng. Khả năng kiểm soát lịch sử commit Git, đặc biệt là trong các tình huống phức tạp như sửa commit đã đẩy lên remote, thể hiện sự trưởng thành và kinh nghiệm của một lập trình viên trong phỏng vấn dev.

Lucas hy vọng bài viết này sẽ giúp các bạn có cái nhìn sâu hơn về Git và chuẩn bị tốt hơn cho các buổi phỏng vấn Git sắp tới. Hãy thực hành thật nhiều để biến kiến thức thành kỹ năng nhé!

Các bài viết liên quan khác của Lucas:

Để lại phản hồi

Địa chỉ email của bạn sẽ không được công bố. Các trường bắt buộc được đánh dấu *