Các lệnh Git nâng cao: Nâng cao kỹ năng quản lý phiên bản với các công cụ mạnh mẽ

Sau khi đã làm quen với các lệnh Git cơ bản, đã đến lúc khám phá những lệnh Git nâng cao để tối ưu hóa quy trình làm việc, giải quyết các tình huống phức tạp, và làm chủ hoàn toàn hệ thống quản lý phiên bản Git. Bài viết này sẽ giới thiệu một số lệnh Git nâng cao dễ hiểu, được sắp xếp logic, giúp bạn mở rộng khả năng sử dụng Git và trở thành một người dùng Git thành thạo hơn.

Danh sách chọn nhanh các lệnh Git nâng cao (Mục lục):

  1. Thay đổi Lịch sử Commit
    1. git rebase
    2. git cherry-pick
    3. git revert
    4. git reset
  2. Kiểm soát Staging Area Nâng cao
    1. git restore –staged
    2. git checkout — (discard changes)
    3. git stash
  3. Kiểm tra và So sánh Thay đổi
    1. git diff
    2. git bisect

1. Thay đổi Lịch sử Commit

Nhóm lệnh này cho phép bạn chỉnh sửa lịch sử commit, thay đổi thứ tự commit, gộp commit, hoặc loại bỏ commit. Lưu ý: Thay đổi lịch sử commit đã push lên remote repository có thể gây ra vấn đề cho người khác trong nhóm, nên cần thận trọng khi sử dụng, đặc biệt khi làm việc nhóm.

a. git rebase

Mô tả: Rebase (tái tạo cơ sở) là một cách tích hợp các thay đổi từ một nhánh vào nhánh khác, tương tự như git merge, nhưng rebase tạo ra một lịch sử commit tuyến tính hơn, sạch sẽ hơn. Rebase hoạt động bằng cách di chuyển các commit của nhánh hiện tại lên trên đỉnh của nhánh mục tiêu.

Ví dụ: Giả sử bạn đang ở nhánh feature-A, muốn tích hợp các thay đổi mới nhất từ nhánh main vào feature-A, bạn có thể dùng:


git checkout feature-A
git rebase main

Giải thích:

  • git checkout feature-A: Chuyển sang nhánh feature-A (nhánh bạn muốn rebase).
  • git rebase main: Rebase nhánh feature-A lên trên đỉnh của nhánh main.
    • Git sẽ tạm thời “gỡ bỏ” các commit của feature-A.
    • Sau đó, Git sẽ “áp dụng lại” từng commit của feature-A, nhưng bắt đầu từ commit mới nhất của main.
  • Kết quả là nhánh feature-A sẽ có lịch sử commit tuyến tính, như thể bạn đã tạo nhánh feature-A từ commit mới nhất của main.

Ưu điểm của rebase:

  • Lịch sử commit sạch sẽ, tuyến tính, dễ đọc và theo dõi.
  • Tránh các nhánh hợp nhất (merge commit) phức tạp trong lịch sử.

Nhược điểm của rebase:

  • Thay đổi lịch sử commit, có thể gây khó khăn cho việc cộng tác nếu đã push nhánh lên remote và người khác đã dựa vào lịch sử đó. Không nên rebase nhánh public (nhánh đã được chia sẻ với người khác).
  • Rebase có thể trở nên phức tạp khi có xung đột (rebase conflicts) xảy ra trong quá trình áp dụng lại commit.

b. git cherry-pick

Mô tả: Cherry-pick (chọn lọc) cho phép bạn chọn một commit cụ thể từ một nhánh khác và áp dụng nó vào nhánh hiện tại. Hữu ích khi bạn chỉ muốn lấy một số thay đổi nhất định từ nhánh khác, thay vì hợp nhất toàn bộ nhánh.

Ví dụ: Giả sử bạn muốn lấy commit có hash abcdef123456 từ nhánh develop và áp dụng vào nhánh feature-B, bạn dùng:


git checkout feature-B
git cherry-pick abcdef123456

Giải thích:

  • git checkout feature-B: Chuyển sang nhánh feature-B (nhánh bạn muốn cherry-pick commit vào).
  • git cherry-pick abcdef123456: Chọn commit có hash abcdef123456 từ nhánh khác và áp dụng nó vào nhánh feature-B.
  • Git sẽ cố gắng áp dụng các thay đổi của commit đó vào nhánh hiện tại. Nếu thành công, một commit mới sẽ được tạo ra trên nhánh feature-B, chứa các thay đổi được cherry-pick.

Lưu ý: Cherry-pick tạo ra một commit mới với cùng nội dung thay đổi, nhưng hash commit sẽ khác với commit gốc. Nếu có xung đột, bạn cần giải quyết xung đột tương tự như khi merge.

c. git revert

Mô tả: Revert (đảo ngược) tạo ra một commit mới để hoàn tác (undo) các thay đổi của một commit đã có. Không giống như git reset (sẽ xóa commit khỏi lịch sử), git revert tạo ra một commit mới “đối nghịch” với commit bạn muốn hoàn tác, giữ lại lịch sử commit gốc.

Ví dụ: Giả sử bạn muốn hoàn tác commit có hash 123456abcdef, bạn dùng:


git revert 123456abcdef

Giải thích:

  • git revert 123456abcdef: Tạo một commit mới để hoàn tác commit 123456abcdef.
  • Git sẽ tự động tạo một commit mới, chứa các thay đổi “ngược lại” với commit 123456abcdef. Ví dụ, nếu commit 123456abcdef thêm dòng code, commit revert sẽ xóa dòng code đó.
  • Commit revert sẽ có thông điệp commit mặc định, bạn có thể chỉnh sửa thông điệp này.

Ưu điểm của revert:

  • An toàn hơn git reset, vì giữ lại lịch sử commit gốc.
  • Dễ dàng hoàn tác commit đã push lên remote và chia sẻ với người khác, vì không thay đổi lịch sử đã public.

Khi nào dùng revert: Khi bạn muốn hoàn tác một commit đã push lên remote hoặc khi bạn muốn giữ lại lịch sử commit đầy đủ.

d. git reset

Mô tả: Reset (đặt lại) cho phép bạn đưa nhánh hiện tại về một commit trước đó. git reset có thể thay đổi staging area, working directory, và lịch sử commit, tùy thuộc vào các tùy chọn bạn sử dụng. Lệnh này rất mạnh mẽ và có thể nguy hiểm nếu không hiểu rõ cách dùng.

Các loại reset (tùy chọn phổ biến):

  • `git reset –soft [commit]`: Soft reset. Đặt lại HEAD về commit chỉ định, nhưng giữ nguyên staging area và working directory. Các thay đổi sau commit chỉ định vẫn được staging. Thường dùng để “gộp” nhiều commit lại thành một commit lớn hơn.
  • `git reset –mixed [commit]` (mặc định nếu không có tùy chọn): Mixed reset. Đặt lại HEAD về commit chỉ định, và reset staging area để khớp với commit chỉ định. Nhưng vẫn giữ nguyên working directory. Các thay đổi sau commit chỉ định sẽ ở trạng thái “not staged” trong working directory. Thường dùng để “bỏ staging” các thay đổi, nhưng vẫn giữ lại các thay đổi trong working directory.
  • `git reset –hard [commit]`: Hard reset. Đặt lại HEAD về commit chỉ định, reset staging area, và reset working directory để hoàn toàn khớp với commit chỉ định. Mọi thay đổi sau commit chỉ định sẽ bị mất hoàn toàn, không thể phục hồi nếu chưa commit hoặc stash. Rất nguy hiểm, cần dùng cẩn thận. Thường dùng để “xóa bỏ” các commit và thay đổi không mong muốn một cách triệt để.

Ví dụ (mixed reset): Giả sử bạn muốn reset về commit trước commit hiện tại (HEAD^), bạn dùng:


git reset --mixed HEAD^

Giải thích:

  • git reset --mixed HEAD^: Reset nhánh hiện tại về commit trước commit HEAD (commit cha).
  • --mixed: Reset HEAD và staging area, nhưng giữ working directory.
  • HEAD^: Tham chiếu đến commit cha của HEAD (commit hiện tại). Bạn cũng có thể dùng hash commit cụ thể (ví dụ: git reset --mixed abcdef123456).
  • Sau khi reset, các thay đổi từ commit HEAD trở về sau sẽ bị “bỏ staging” và nằm trong working directory ở trạng thái “not staged”. Bạn có thể commit lại hoặc bỏ chúng đi.

Cảnh báo: `git reset –hard` rất nguy hiểm, có thể mất dữ liệu vĩnh viễn nếu bạn không cẩn thận. Hãy chắc chắn bạn hiểu rõ mình đang làm gì trước khi dùng git reset --hard, đặc biệt là khi reset về commit quá khứ xa xôi.


2. Kiểm soát Staging Area Nâng cao

Nhóm lệnh này giúp bạn kiểm soát staging area một cách linh hoạt hơn, bỏ staging các thay đổi, hoặc tạm thời cất giữ các thay đổi chưa commit.

a. git restore –staged [tên file]

Mô tả: Bỏ staging một file, tức là chuyển file từ staging area trở lại working directory ở trạng thái “not staged”. Hữu ích khi bạn đã staging nhầm file hoặc muốn loại bỏ một số thay đổi khỏi staging area trước khi commit.

Ví dụ: Giả sử bạn đã staging file script.js nhưng muốn bỏ staging nó, bạn dùng:


git restore --staged script.js

Giải thích:

  • git restore --staged script.js: Bỏ staging file script.js.
  • Sau khi lệnh này chạy, file script.js sẽ không còn ở staging area nữa, mà trở lại working directory ở trạng thái “Changes not staged for commit” (như khi bạn dùng git status kiểm tra).
  • Các thay đổi trong file script.js vẫn còn trong working directory, bạn có thể chỉnh sửa tiếp, staging lại, hoặc bỏ chúng đi.

b. git checkout — [tên file] (discard changes)

Mô tả: Discard changes (loại bỏ thay đổi) trong working directory cho một file cụ thể. Lệnh này sẽ khôi phục file trong working directory về phiên bản mới nhất trong staging area hoặc commit HEAD (nếu chưa staging). Mọi thay đổi chưa staging trong file sẽ bị mất.

Ví dụ: Giả sử bạn đã chỉnh sửa file style.css trong working directory và muốn bỏ hết các thay đổi đó, khôi phục về phiên bản gốc, bạn dùng:


git checkout -- style.css

Giải thích:

  • git checkout -- style.css: Loại bỏ các thay đổi trong file style.css ở working directory.
  • --: Dấu phân cách để Git hiểu rằng style.css là tên file, không phải tên branch.
  • Cảnh báo: Mọi thay đổi bạn đã thực hiện trong file style.css (kể từ commit hoặc staging gần nhất) sẽ bị mất vĩnh viễn. Hãy chắc chắn bạn muốn bỏ các thay đổi trước khi dùng lệnh này.

Khi nào dùng `git checkout — [file]`: Khi bạn muốn hủy bỏ nhanh chóng các thay đổi chưa staging trong một file và quay lại phiên bản gốc.

c. git stash

Mô tả: Stash (cất giữ) các thay đổi chưa commit của bạn. Stash giúp bạn tạm thời cất giữ các thay đổi trong working directory và staging area, để có thể chuyển sang nhánh khác làm việc, hoặc làm sạch working directory. Các thay đổi được stash sẽ được lưu vào một “ngăn chứa tạm thời” (stash stack) và bạn có thể khôi phục lại sau.

Các lệnh stash phổ biến:

  • `git stash`: Stash các thay đổi hiện tại. Sẽ stash cả staging area và working directory.
  • `git stash save “[Thông điệp stash]”`: Stash và đặt tên cho stash. Thông điệp giúp bạn dễ dàng nhớ stash này chứa gì.
  • `git stash list`: Xem danh sách các stash đã lưu.
  • `git stash apply`: Áp dụng stash mới nhất vào nhánh hiện tại. Stash vẫn còn trong stash list sau khi apply.
  • `git stash pop`: Áp dụng stash mới nhất và xóa nó khỏi stash list. Thường dùng khi bạn muốn khôi phục stash và không cần giữ lại bản stash nữa.
  • `git stash apply stash@{index}` hoặc `git stash pop stash@{index}`: Áp dụng hoặc pop một stash cụ thể từ stash list, thay vì stash mới nhất. Index là số thứ tự của stash trong danh sách (bắt đầu từ 0).
  • `git stash drop stash@{index}`: Xóa một stash cụ thể khỏi stash list.
  • `git stash clear`: Xóa tất cả các stash trong stash list.

Ví dụ (stash và pop):


# ... Bạn đang làm việc và có các thay đổi chưa commit ...
git stash save "WIP: Dở dang tính năng X" # Stash và đặt tên
Saved working directory and index state WIP: Dở dang tính năng X
HEAD is now at abcdef123 Merge branch 'main' into develop
... Bây giờ working directory của bạn đã sạch sẽ, bạn có thể chuyển nhánh khác ...
git checkout main

... Sau khi làm xong việc khác ở nhánh main, bạn muốn quay lại làm tiếp tính năng X ...
git checkout feature-X
git stash pop # Áp dụng và xóa stash mới nhất

Khi nào dùng stash:

  • Khi bạn đang làm việc dở dang một tính năng, nhưng cần chuyển sang nhánh khác để sửa lỗi khẩn cấp hoặc làm việc khác.
  • Khi bạn muốn làm sạch working directory tạm thời, nhưng không muốn commit các thay đổi dở dang.

3. Kiểm tra và So sánh Thay đổi

Nhóm lệnh này giúp bạn kiểm tra các thay đổi, so sánh giữa các phiên bản, và tìm kiếm lỗi trong lịch sử dự án.

a. git diff

Mô tả: Diff (difference – khác biệt) dùng để so sánh các thay đổi giữa các phiên bản khác nhau của code. git diff có thể so sánh:

  • Working directory vs. Staging area: Xem các thay đổi trong working directory so với staging area (những thay đổi chưa staging). (Sử dụng: git diff)
  • Staging area vs. Commit gần nhất (HEAD): Xem các thay đổi đã staging so với commit HEAD. (Sử dụng: git diff --staged hoặc git diff --cached)
  • Giữa hai commit, hai nhánh, hoặc hai file cụ thể: (Ví dụ: git diff HEAD~2 HEAD, git diff main feature-A, git diff file1.txt file2.txt)

Ví dụ (so sánh working directory vs. staging area):


git diff

Giải thích:

  • Lệnh git diff không có tùy chọn sẽ so sánh working directory với staging area.
  • Kết quả sẽ hiển thị các dòng code đã được thêm vào (bắt đầu bằng +) và dòng code đã bị xóa (bắt đầu bằng -) trong working directory so với staging area.
  • Diff output thường được hiển thị theo định dạng unified diff, dễ đọc và so sánh code.

Ví dụ (so sánh staging area vs. commit HEAD):


git diff --staged

Giải thích:

  • git diff --staged hoặc git diff --cached sẽ so sánh staging area với commit HEAD.
  • Hiển thị các thay đổi đã được staging và sẽ được commit trong commit tiếp theo.

b. git bisect

Mô tả: Bisect (chia đôi) là một công cụ mạnh mẽ để tìm ra commit nào đã gây ra lỗi (bug) trong lịch sử commit của dự án. git bisect sử dụng thuật toán tìm kiếm nhị phân để nhanh chóng xác định commit gây lỗi.

Quy trình dùng `git bisect`:

  1. Bắt đầu bisect: git bisect start
  2. Đánh dấu commit “tốt” (không có lỗi): git bisect good [commit tốt] (thường là commit cũ hơn, biết chắc là không có lỗi).
  3. Đánh dấu commit “xấu” (có lỗi): git bisect bad [commit xấu] (thường là commit hiện tại hoặc commit mới hơn, commit mà bạn phát hiện ra lỗi).
  4. Git sẽ tự động checkout một commit ở giữa khoảng “tốt” và “xấu”.
  5. Kiểm tra commit hiện tại: Chạy chương trình, test, hoặc kiểm tra lỗi.
    • Nếu commit hiện tại “tốt” (không có lỗi): git bisect good
    • Nếu commit hiện tại “xấu” (có lỗi): git bisect bad
  6. Lặp lại bước 5. Git sẽ tiếp tục chia đôi khoảng tìm kiếm và checkout commit tiếp theo.
  7. Sau một số bước lặp, `git bisect` sẽ xác định được commit đầu tiên gây ra lỗi.
  8. Kết thúc bisect: git bisect reset (để quay lại nhánh làm việc ban đầu).

Ví dụ: Giả sử bạn phát hiện ra lỗi ở commit hiện tại, và biết commit v1.0 (tag v1.0) là commit tốt, không có lỗi. Bạn có thể dùng git bisect như sau:


git bisect start
git bisect good v1.0
git bisect bad HEAD # HEAD là commit hiện tại (xấu)
... Git checkout một commit ở giữa ...
... Kiểm tra chương trình ...
Giả sử commit này vẫn "xấu"
git bisect bad

... Git checkout commit tiếp theo ...
... Kiểm tra chương trình ...
Giả sử commit này "tốt"
git bisect good

... Lặp lại cho đến khi git bisect xác định commit gây lỗi ...
... Git thông báo commit gây lỗi: "First bad commit: [hash] ..."
git bisect reset # Kết thúc bisect, quay lại nhánh ban đầu

Khi nào dùng `git bisect`: Khi bạn biết rằng lỗi mới xuất hiện gần đây, và muốn tìm ra commit nào đã gây ra lỗi một cách nhanh chóng trong lịch sử commit dài.


Kết luận: Sử dụng lệnh Git nâng cao để làm chủ quy trình phát triển

Bài viết này đã giới thiệu một số lệnh Git nâng cao, giúp bạn mở rộng khả năng sử dụng Git và giải quyết các tình huống phức tạp hơn trong quá trình phát triển phần mềm. Từ việc chỉnh sửa lịch sử commit (rebase, cherry-pick, revert, reset), kiểm soát staging area (restore --staged, checkout --, stash), đến kiểm tra và so sánh thay đổi (diff, bisect), các lệnh này cung cấp cho bạn những công cụ mạnh mẽ để quản lý phiên bản code một cách chuyên nghiệp.

Hãy thực hành các lệnh này, thử nghiệm trong các dự án cá nhân, và dần dần tích hợp chúng vào quy trình làm việc của bạn. Càng sử dụng thành thạo các lệnh Git nâng cao, bạn càng trở nên tự tin và hiệu quả hơn trong vai trò lập trình viên!

Chúc bạn tiếp tục thành công trên con đường chinh phục Git và phát triển sự nghiệp lập trình!

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Lên đầu trang