Tình huống ai cũng gặp: bạn đang sửa dở một tính năng, code chưa chạy, chưa muốn commit "nửa vời" — thì có việc gấp phải nhảy sang nhánh khác (sửa bug production). Commit dở thì bẩn lịch sử, mà đổi nhánh với thay đổi chưa lưu thì Git chặn hoặc kéo theo lung tung. git stash sinh ra cho đúng lúc này: cất thay đổi vào một ngăn tạm, trả working tree về sạch, để bạn làm việc khác rồi quay lại lấy ra tiếp. Đơn giản nhưng có vài bẫy đáng biết. Đo thật bằng git 2.39.

Cất và lấy lại

git stash gom các thay đổi đã theo dõi (tracked) vào ngăn tạm và khôi phục working tree về trạng thái của commit gần nhất:

git stash          # cất thay đổi đã theo dõi
git stash list     # xem: stash@{0}, stash@{1}...
git stash pop      # lấy stash mới nhất ra VÀ xóa khỏi list
git stash apply    # lấy ra nhưng GIỮ lại trong list
git stash -u       # cất CẢ file chưa theo dõi (untracked)
git stash drop     # xóa một stash; git stash -p cất từng phần

Ảnh chụp đoạn mã nền tối giải thích git stash cất tạm thay đổi dở dang để đổi việc gấp, tình huống đang sửa dở thì cần đổi nhánh gấp bạn sửa dở chưa muốn commit nhưng phải checkout nhánh khác sửa bug gấp đổi nhánh với thay đổi dở dễ bị chặn xung đột git stash cất thay đổi vào ngăn tạm working tree sạch lại làm việc khác xong quay lại lấy ra tiếp, các lệnh stash thường dùng git stash cất thay đổi đã theo dõi bỏ qua untracked git stash -u cất cả file chưa theo dõi git stash list xem các stash git stash pop lấy stash mới nhất ra và xóa khỏi list git stash apply lấy ra nhưng giữ lại trong list git stash drop xóa một stash git stash -p cất từng phần, bản chất stash là commit ẩn mỗi stash thực ra là commit gắn vào ref refs stash không nằm trên nhánh nào giống reflog gc dọn sau nếu bị drop bẫy stash không mã hóa không đồng bộ là ngăn tạm cục bộ

Hình 1: git stash cất thay đổi vào ngăn tạm để working tree sạch lại. Các lệnh stash/list/pop/apply/-u/drop. Bản chất mỗi stash là commit ẩn gắn vào refs/stash, không nằm trên nhánh nào.

Đo thật: bẫy untracked, và pop vs apply

Ảnh chụp bảng kết quả đo thật nền tối chạy git 2.39, phần một git stash cất thay đổi đã theo dõi trước M a.txt hai dấu hỏi c.txt git stash Saved working directory WIP on master sau còn hai dấu hỏi c.txt a.txt sạch c.txt untracked vẫn còn, phần hai git stash -u cất cả untracked sạch hoàn toàn git stash -u Saved sau working tree sạch c.txt cũng được cất, phần ba apply vs pop apply giữ stash trong list stash@0 stash@1 hai stash git stash apply lấy lại thay đổi list sau apply stash@0 stash@1 vẫn còn pop thì mất, phần bốn git stash drop xóa một stash git stash drop Dropped refs stash@0 6ba3fd41 list còn stash@0 đã bớt một

Hình 2: git stash cất a.txt (tracked) nhưng c.txt (untracked) vẫn còn — phải git stash -u mới cất cả untracked. git stash apply lấy lại thay đổi nhưng stash vẫn trong list (stash@{0}, stash@{1}), khác pop (xóa luôn). git stash drop xóa một stash.

Kết quả làm rõ ba điểm:

  • Cất và lấy: git stash đưa M a.txt vào ngăn tạm, working tree sạch; git stash pop lấy lại đúng thay đổi đó.
  • Bẫy untracked: git stash mặc định bỏ qua file chưa theo dõi — sau khi stash, c.txt (untracked) vẫn còn trong working tree. Nếu bạn tưởng stash đã cất mọi thứ rồi checkout nhánh khác, c.txt sẽ đi theo và gây bối rối. Muốn cất cả untracked, dùng git stash -u (sau đó working tree sạch hoàn toàn). Thêm -a (--all) nếu muốn cất cả file bị .gitignore.
  • pop vs apply: cả hai lấy thay đổi ra, nhưng pop xóa stash khỏi list sau khi lấy, còn apply giữ lại (đo thật, sau apply vẫn thấy stash@{0}, stash@{1}). Dùng apply khi muốn áp cùng stash lên nhiều nhánh; dùng pop cho luồng thông thường. Dọn bằng git stash drop.

Đánh đổi và lưu ý

Stash là commit ẩn — mất được nhưng cứu được như reflog. Mỗi stash thực chất là một (hoặc vài) commit gắn vào ref refs/stash, không nằm trên nhánh nào. Nếu lỡ drop nhầm, nó thành commit mồ côi — vẫn cứu được qua git fsck --no-reflog | grep commit rồi git stash apply <sha>, cho tới khi gc dọn. Nhưng đừng dựa vào đó.

Đừng dùng stash làm nơi lưu trữ lâu dài. Stash là ngăn tạm cục bộ: không đồng bộ khi push, không mã hóa, dễ quên. Một stash "để mai làm tiếp" nằm hàng tuần rồi xung đột lung tung khi pop là chuyện thường. Nếu công việc đáng giữ lâu, tạo nhánh (git switch -c wip/tinh-nang) và commit — rõ ràng và an toàn hơn nhiều.

git stash -p để cất chọn lọc. Nếu chỉ muốn cất một phần thay đổi (giữ lại phần khác để tiếp tục), git stash -p (patch) cho chọn từng đoạn (hunk) — giống git add -p. Hữu ích khi bạn muốn tách một thử nghiệm ra khỏi công việc chính.

Ba ý mang về

  1. git stash cất thay đổi vào ngăn tạm để đổi nhánh/làm việc gấp mà không commit dở, rồi lấy lại — đo thật, stash a.txt xong working tree sạch, pop lấy lại nguyên vẹn.
  2. git stash bỏ qua file untracked (đo thật c.txt vẫn còn) — phải git stash -u để cất cả untracked; apply giữ stash trong list còn pop xóa (dùng apply khi áp lên nhiều nhánh).
  3. Stash là commit ẩn, ngăn tạm cục bộ: lỡ drop vẫn cứu được như reflog (tới khi gc), nhưng đừng dùng làm lưu trữ lâu dài — việc đáng giữ thì tạo nhánh + commit; stash -p để cất chọn lọc.

Nguồn

Phần sau ta xử lý cùng bài toán "làm nhiều việc song song" theo cách mạnh hơn stash: git worktree — một kho .git nhưng nhiều thư mục làm việc, mỗi cái ở một nhánh khác nhau.