Git Stash: Giải Pháp Nhanh Gọn Khi Công Việc Dang Dở Gặp Vấn Đề Cấp Bách

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Tình huống ‘dở khóc dở cười’: Đang say sưa code, sếp gọi gấp!

Hai giờ sáng, màn hình server báo lỗi đỏ lòm, tin nhắn Slack của sếp liên tục réo. Tim đập thình thịch. Mình đang phát triển một tính năng mới trên branch feature/abc, code còn ngổn ngang, chưa hoàn thiện, chưa thể commit. Nhưng lỗi production thì không chờ đợi. Làm sao để “dọn dẹp” mớ code dang dở này thật nhanh, quay về branch main fix bug mà không làm mất công sức vừa rồi?

Nếu bạn từng rơi vào tình huống này, hoặc gặp phải các trường hợp tương tự như:

  • Cần pull code mới từ main nhưng branch hiện tại của bạn đang có những thay đổi chưa commit.
  • Muốn chuyển qua branch khác để kiểm tra gì đó rồi quay lại làm việc.
  • Thử nghiệm một ý tưởng mới mà không muốn commit ngay những thay đổi thử nghiệm đó.

Đây chính là lúc bạn cần một giải pháp để quản lý hiệu quả những thay đổi dang dở đó. Không gian làm việc của bạn (working directory) đang “bẩn” với các file đã chỉnh sửa (modified) hoặc được thêm mới (staged), và bạn cần một cách để “cất” chúng đi tạm thời.

So sánh các “cách chữa cháy” tức thời

Trước khi đến với giải pháp hiệu quả nhất, hãy điểm qua một số cách giải quyết tức thời mà nhiều anh em dev vẫn thường áp dụng. Chắc chắn sẽ có những tình huống khiến bạn phải bật cười:

Approach 1: Commit đại một cái (WIP commit)

Cách làm: Nhanh tay git add . rồi git commit -m "WIP: XXXXX" một cái cho xong chuyện. Coi như là đã lưu lại công việc, rồi về main fix bug.

Ưu điểm:

  • Cực kỳ nhanh gọn, không tốn thời gian suy nghĩ.
  • Code của bạn được lưu vào lịch sử Git, không lo mất mát.

Nhược điểm:

  • Làm bẩn lịch sử commit: Bạn vừa thêm một commit vô nghĩa, chưa hoàn thiện vào branch của mình. Sau này sẽ mất công dùng git rebase -i để dọn dẹp, dễ gây nhầm lẫn nếu không quen.
  • Khó quản lý: Nếu có nhiều WIP commit kiểu này, việc theo dõi xem commit nào thuộc tính năng nào, đã làm đến đâu sẽ rất rối.
  • Rủi ro khi merge/rebase: Những commit dở dang này có thể gây xung đột không đáng có khi bạn merge hoặc rebase branch sau này.

Approach 2: Tạo branch mới để giữ code

Cách làm: Tạo một branch mới (ví dụ git branch fix-prod-issue), sau đó commit những thay đổi hiện tại vào branch đó. Sau khi xử lý xong xuôi bên main, bạn quay lại branch này để tiếp tục làm việc.

Ưu điểm:

  • Sạch sẽ lịch sử: Branch feature/abc của bạn không bị làm bẩn bởi các commit dở dang.
  • An toàn: Code của bạn được lưu trữ an toàn trong một branch riêng biệt.

Nhược điểm:

  • Hơi rườm rà: Đối với việc chỉ cần “lưu tạm” trong vài phút hoặc vài giờ, việc tạo một branch mới rồi sau này phải merge hoặc rebase lại có vẻ hơi quá mức cần thiết.
  • Quản lý nhiều branch: Nếu bạn thường xuyên làm cách này, bạn sẽ có rất nhiều branch tạm thời cần phải dọn dẹp sau này.

Approach 3: Copy paste code ra chỗ khác (TRỜI ƠI!)

Cách làm: Mở File Explorer/Finder, copy toàn bộ folder project hoặc những file đã chỉnh sửa ra một thư mục tạm trên desktop hoặc ổ đĩa khác. Sau đó dùng git reset --hard để quay về trạng thái sạch.

Ưu điểm:

  • Cực kỳ nhanh gọn nếu bạn chỉ có vài file cần xử lý.
  • Không cần nhớ các lệnh Git phức tạp.

Nhược điểm:

  • Rủi ro mất code cực cao: Bạn đang đánh cược với số phận! Quên copy file nào đó? Format ổ đĩa? Xóa nhầm? Toang!
  • Không có version control: Đây không phải là Git! Mọi thay đổi đều là bản copy rời rạc, không thể theo dõi hay phục hồi phiên bản.
  • Thảm họa khi project lớn: Copy một dự án hàng GB (ví dụ một dự án frontend với thư mục node_modules khổng lồ) là một cực hình, chưa kể việc sau này copy lại vào project cũ rất dễ gây ghi đè nhầm hoặc thiếu file.

Bản thân mình từng mất code quan trọng vì force push nhầm branch. Từ đó, mình luôn cẩn thận với git push --force. Trải nghiệm đó giúp mình thấm thía tầm quan trọng của việc giữ codebase sạch và có phương án dự phòng an toàn cho công việc dang dở, như git stash. Công cụ này giúp mình tránh vội vàng commit những thứ chưa xong, giảm thiểu rủi ro không đáng có.

Approach 4: Git stash – Công cụ bạn cần

Đây chính là tính năng mà Git cung cấp để giải quyết chính xác vấn đề này: lưu tạm công việc đang dở, làm sạch không gian làm việc, giúp bạn dễ dàng chuyển đổi ngữ cảnh mà không cần commit.

Phân tích ưu nhược điểm của Git stash

Ưu điểm của Git stash

  • Giữ không gian làm việc sạch sẽ: Ngay lập tức “cất” những thay đổi chưa commit đi, đưa branch về trạng thái của commit HEAD gần nhất.
  • Dễ dàng chuyển đổi ngữ cảnh: Bạn có thể nhanh chóng chuyển sang branch khác, pull code mới, hoặc fix bug mà không lo ảnh hưởng đến công việc đang dở.
  • Không làm bẩn lịch sử commit: Các thay đổi được lưu vào một “ngăn kéo” riêng của Git, không xuất hiện trong lịch sử commit của branch.
  • Quản lý nhiều stash: Git cho phép bạn lưu nhiều “ngăn kéo” (stash) khác nhau, mỗi stash đại diện cho một lần lưu công việc.
  • Lưu cả file untracked (với option): Mặc định Git stash chỉ lưu file đã tracked, nhưng bạn có thể yêu cầu nó lưu cả những file mới tạo chưa được git add.

Nhược điểm của Git stash

  • Dễ quên stash: Nếu bạn không đặt tên cho stash hoặc không thường xuyên kiểm tra, rất dễ quên mất mình đã stash cái gì và ở đâu.
  • Xung đột có thể xảy ra: Khi bạn áp dụng lại một stash (stash apply hoặc stash pop) vào một branch đã có những thay đổi khác biệt so với lúc stash, xung đột merge (merge conflict) có thể xảy ra.
  • Không áp dụng được giữa các repo khác nhau: Stash chỉ tồn tại cục bộ trong repository hiện tại của bạn. Bạn không thể push stash lên remote hoặc chuyển nó sang một repo khác.

Chọn cách phù hợp: Khi nào thì dùng Git stash?

Từ những phân tích trên, ta thấy git stash là giải pháp lý tưởng cho các tình huống cần tạm ngưng công việc mà không muốn commit. Bạn nên dùng git stash khi:

  • Cần pull/merge code mới: Branch hiện tại của bạn có những thay đổi chưa commit, và bạn cần kéo code mới từ main về. git pull sẽ báo lỗi nếu có thay đổi chưa commit.
  • Cần chuyển sang branch khác để làm việc khẩn cấp: Như tình huống fix bug production lúc 2 giờ sáng ở đầu bài viết.
  • Muốn chạy test trên code gốc: Bạn đang thử nghiệm một tính năng mới và muốn kiểm tra lại xem các test cũ có còn pass trên phiên bản code sạch không.
  • Thử nghiệm một ý tưởng: Bạn muốn thử một thay đổi nhỏ nhưng không chắc chắn và không muốn commit nó ngay lập tức. Stash lại, thử, nếu không ổn thì drop stash đi.

Tóm lại, nếu bạn cần một không gian làm việc sạch sẽ mà không muốn mất đi công sức đang dở, git stash chính là lựa chọn phù hợp.

Hướng dẫn triển khai Git stash từ A đến Z

Giờ thì, cùng thực hành các lệnh git stash để thành thạo công cụ hữu ích này nhé.

git stash save "message" hoặc git stash: Lưu lại công việc

Đây là lệnh cơ bản nhất để cất các thay đổi đang có trong working directory và staging area của bạn vào một stash. Bạn nên kèm theo một message để dễ nhớ sau này.


# Lưu lại tất cả các file đã modified và staged
# Nên kèm theo message để dễ nhớ
git stash save "Dang lam feature XYZ, can fix prod bug gap"

# Cách viết ngắn gọn hơn, không có message
# Git sẽ tự động tạo message dựa trên commit HEAD
git stash

Sau khi chạy lệnh này, working directory của bạn sẽ sạch bóng như lúc vừa clone project về hoặc vừa checkout một commit. Các thay đổi của bạn đã được Git đóng gói và cất vào ngăn kéo.

git stash list: Xem danh sách stash

Để kiểm tra xem bạn đã stash những gì, lệnh này sẽ hiển thị danh sách tất cả các stash mà bạn đã lưu. Mỗi stash sẽ có một index dạng stash@{n}, trong đó n là số thứ tự (stash gần nhất là 0).


git stash list
# Kết quả có thể trông như thế này:
# stash@{0}: On feature/abc: Dang lam feature XYZ, can fix prod bug gap
# stash@{1}: On main: Hotfix: try out new logic for auth

git stash apply [stash@{n}]: Áp dụng lại stash, giữ nguyên trong danh sách

Lệnh này sẽ lấy các thay đổi từ stash và áp dụng chúng vào working directory hiện tại của bạn. Stash đó vẫn sẽ còn trong danh sách stash.


# Áp dụng stash gần nhất (stash@{0})
git stash apply

# Áp dụng một stash cụ thể (ví dụ stash@{1})
git stash apply stash@{1}

Sử dụng apply khi bạn muốn thử áp dụng stash nhưng vẫn muốn giữ nó trong danh sách phòng khi cần dùng lại hoặc áp dụng cho branch khác.

git stash pop [stash@{n}]: Áp dụng lại stash và xóa khỏi danh sách

Tương tự như apply, nhưng pop sẽ xóa stash đó khỏi danh sách ngay sau khi áp dụng thành công. Đây là cách dùng phổ biến nhất khi bạn đã chắc chắn không cần stash đó nữa.


# Áp dụng và xóa stash gần nhất
git stash pop

# Áp dụng và xóa một stash cụ thể
git stash pop stash@{1}

Nếu có xung đột khi pop, Git sẽ không xóa stash đó khỏi danh sách. Bạn phải giải quyết xung đột trước, sau đó có thể git stash drop thủ công.

git stash drop [stash@{n}]: Xóa một stash cụ thể

Nếu bạn quyết định không cần một stash nào đó nữa, hãy dùng lệnh này để xóa nó khỏi danh sách.


# Xóa stash gần nhất
git stash drop

# Xóa một stash cụ thể
git stash drop stash@{1}

git stash clear: Xóa tất cả stash

Cẩn thận khi dùng lệnh này! Nó sẽ xóa tất cả các stash của bạn mà không có xác nhận.


git stash clear

Chỉ sử dụng khi bạn chắc chắn rằng mình không còn cần bất kỳ stash nào nữa.

git stash show [stash@{n}]: Xem nội dung stash

Nếu bạn quên mất một stash có gì bên trong, lệnh show sẽ hiển thị sự khác biệt (diff) của stash đó so với commit gốc.


# Xem nội dung stash gần nhất
git stash show

# Xem nội dung chi tiết (full diff) của stash gần nhất
git stash show -p

# Xem nội dung của một stash cụ thể
git stash show stash@{1}

git stash branch <branchname> [stash@{n}]: Tạo branch mới từ stash

Lệnh này cực kỳ hữu ích. Nếu bạn đã stash một tập hợp thay đổi và muốn phát triển chúng thành branch độc lập, lệnh này sẽ tạo branch mới, áp dụng stash vào đó, rồi xóa stash khỏi danh sách.


# Tạo branch "new-feature-from-stash" từ stash gần nhất
git stash branch new-feature-from-stash

# Tạo branch "hotfix-from-old-stash" từ stash@{1}
git stash branch hotfix-from-old-stash stash@{1}

Những lưu ý quan trọng khi dùng Git stash

  • Giải quyết xung đột: Khi git stash apply hoặc git stash pop, nếu Git không thể tự động merge các thay đổi, bạn sẽ gặp xung đột. Hãy giải quyết xung đột như khi merge/rebase thông thường, sau đó git add .git commit để hoàn tất.
  • File untracked: Mặc định, git stash chỉ lưu các file đã được Git theo dõi (tracked files), đã có thay đổi (modified) hoặc ở staging area (staged). Nó không stash các file mới tạo chưa được git add. Để stash cả các file untracked, bạn có thể dùng git stash -u hoặc git stash --include-untracked. Để stash cả file untracked và file bị Git bỏ qua (ignored files, ví dụ .env, node_modules), hãy dùng git stash -a hoặc git stash --all.
  • Quản lý stash list thường xuyên: Tránh để danh sách stash của bạn trở nên quá dài và khó kiểm soát. Hãy xóa những stash không còn cần thiết bằng git stash drop.
  • Stash chỉ là cục bộ: Đừng quên rằng stash chỉ tồn tại trên máy tính của bạn, không được đẩy lên remote repository. Do đó, nó không phải là một giải pháp sao lưu lâu dài.

Git stash không chỉ là một lệnh đơn thuần; nó là một công cụ giúp bạn duy trì môi trường phát triển gọn gàng và luôn sẵn sàng ứng phó với các tình huống khẩn cấp. Cá nhân mình cảm thấy tự tin hơn rất nhiều khi làm việc trong những dự án yêu cầu tốc độ và sự linh hoạt. Đừng để những thay đổi dang dở làm chậm bạn khi hệ thống đang gặp sự cố. Hãy thành thạo git stash!

Share: