git stash: cách cất tạm thay đổi và lấy lại
git stash chuyển các thay đổi chưa commit vào một kho riêng và đưa thư mục làm việc về commit cuối, để bạn chuyển sang việc khác. Lấy lại bằng git stash pop (áp dụng rồi xóa khỏi kho) hoặc git stash apply (áp dụng và giữ lại); lệnh git unstash không tồn tại.
Vì sao cần git stash
Tình huống quen thuộc: bạn đang làm dở một việc, tệp đã thay đổi, chưa đến lúc commit — và bỗng có người nhờ sửa gấp một lỗi ở nhánh khác. Chuyển nhánh không được, hoặc bạn sợ mất các thay đổi. git stash cất mọi thay đổi chưa commit «vào túi» và đưa thư mục làm việc về trạng thái của commit cuối.
$ git stash
Saved working directory and index state WIP on feature/login: 3f2a1c4 Form đăng nhập
$ git status
On branch feature/login
nothing to commit, working tree clean
Bây giờ có thể chuyển nhánh, sửa lỗi, rồi quay lại và lấy phần đã cất ra. Mỗi lần cất là một bản ghi trong ngăn xếp stash; bên trong Git lưu nó như một commit đặc biệt, nên dữ liệu đáng tin cậy và không phụ thuộc vào nhánh.
Cách cất thay đổi: các tùy chọn chính
Kèm mô tả dễ hiểu
Mô tả mặc định «WIP on nhánh» sau một tuần sẽ chẳng nói lên điều gì. Hãy ghi chú:
git stash push -m "kiểm tra form, chưa làm xong e-mail"
git stash không tham số là cách viết tắt của git stash push. Dạng cũ git stash save "mô tả" vẫn chạy, nhưng trong các tình huống mới hãy dùng push -m.
Kèm tệp mới
Mặc định stash chỉ lấy các tệp đã được theo dõi — đã sửa hoặc đã đưa vào index. Tệp mới chưa từng git add sẽ ở lại thư mục làm việc. Đây là điều bất ngờ thường gặp.
git stash -u # --include-untracked: cộng thêm tệp chưa theo dõi
git stash -a # --all: cộng thêm cả tệp trong .gitignore
Với -a cần thận trọng: node_modules, sản phẩm build và cấu hình cục bộ cũng sẽ bị cất đi. Thường -u là đủ.
Chỉ một phần thay đổi
git stash push -m "cấu hình" src/config.js # chỉ các tệp được chỉ định
git stash -p # chọn từng đoạn một cách tương tác
git stash --staged # chỉ phần đã nằm trong index (Git 2.35+)
git stash --keep-index # cất tất cả nhưng giữ index trong thư mục
--keep-index hữu ích trước khi commit: bạn giữ lại trong thư mục làm việc đúng những gì sẽ commit, chạy test và kiểm tra commit đó đủ độc lập.
Cách lấy lại thay đổi (git unstash)
Lệnh git unstash không tồn tại trong Git — người ta hay tìm kiếm nó khi muốn lấy lại thứ đã cất. Thực tế có hai lệnh.
| Lệnh | Tác dụng | Bản ghi trong ngăn xếp |
|---|---|---|
git stash pop | Áp dụng thay đổi cất gần nhất | Bị xóa nếu áp dụng thành công không có xung đột |
git stash apply | Áp dụng thay đổi cất gần nhất | Được giữ lại |
git stash pop stash@{2} | Áp dụng một bản ghi cụ thể | Bị xóa |
git stash apply --index | Khôi phục cả phần index lẫn thư mục làm việc | Được giữ lại |
$ git stash pop
On branch feature/login
Changes not staged for commit:
modified: src/auth.js
modified: src/form.js
Dropped refs/stash@{0} (8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d)
Lưu ý: mặc định ngay cả những gì đã nằm trong index tại thời điểm cất cũng được trả lại như thay đổi thông thường. Nếu cần giữ nguyên index, thêm --index.
pop hay apply
Trong công việc hằng ngày, pop tiện hơn: lấy ra và quên đi. Còn apply phù hợp khi bạn muốn áp dụng cùng một thay đổi vào nhiều nhánh, hoặc khi chưa chắc mọi thứ sẽ suôn sẻ — bản ghi vẫn còn như một tấm lưới an toàn, và có thể xóa sau bằng git stash drop.
Làm việc với danh sách stash
$ git stash list
stash@{0}: On feature/login: kiểm tra form, chưa làm xong e-mail
stash@{1}: WIP on main: 9b7e0d2 Cập nhật phụ thuộc
stash@{2}: On feature/api: thử nghiệm bộ nhớ đệm
Ngăn xếp hoạt động theo nguyên tắc «vào sau ra trước»: stash@{0} luôn là bản ghi mới nhất, và các số thứ tự dịch chuyển sau mỗi lần push hoặc drop.
| Lệnh | Mục đích |
|---|---|
git stash show | Thống kê ngắn gọn về bản ghi gần nhất |
git stash show -p stash@{1} | Diff đầy đủ của một bản ghi cụ thể |
git stash drop stash@{1} | Xóa một bản ghi |
git stash clear | Xóa toàn bộ bản ghi cùng lúc |
git stash branch tên stash@{0} | Tạo nhánh từ commit nơi đã cất, áp dụng bản ghi và xóa nó |
Để git stash show mặc định hiển thị diff đầy đủ thay vì thống kê, và cả các tệp chưa theo dõi, có thể đặt cấu hình stash.showPatch và stash.showIncludeUntracked.
Kịch bản từng bước: lỗi gấp giữa lúc đang làm việc
- Cất công việc hiện tại cùng tệp mới:
git stash push -u -m "đăng nhập: bản nháp". - Chuyển sang nhánh chính và tạo nhánh sửa lỗi:
git switch main,git pull,git switch -c hotfix/null-email. - Sửa lỗi, commit, gửi đi review.
- Quay lại công việc:
git switch feature/login. - Lấy thay đổi về:
git stash pop. Kiểm tra bằnggit status— mọi thứ phải còn nguyên.
Hiện nay nhiều người dùng git worktree cho kịch bản này: một thư mục làm việc thứ hai của cùng kho cho phép hoàn toàn không cần cất gì. Nhưng để chuyển việc trong năm phút, stash vẫn đơn giản hơn.
Xung đột khi áp dụng stash
Nếu sau khi cất, nhánh đã thay đổi đúng những dòng đó, việc áp dụng sẽ dẫn đến xung đột:
$ git stash pop
Auto-merging src/auth.js
CONFLICT (content): Merge conflict in src/auth.js
The stash entry is kept in case you need it again.
Trong trường hợp này bản ghi không bị xóa, kể cả khi dùng pop. Giải quyết xung đột trong tệp như thường lệ, sau đó chạy git add để đánh dấu đã xử lý (hoặc git restore --staged nếu không muốn giữ thay đổi trong index), rồi xóa bản ghi không còn cần bằng git stash drop.
Nếu xung đột phức tạp, có cách tắt đường: git stash branch tmp-login. Git tạo nhánh từ chính commit nơi bạn đã cất, và thay đổi được áp dụng ở đó không có xung đột; sau đó gộp nhánh này vào nhánh chính bằng merge thông thường.
Khôi phục stash đã xóa nhầm
Lỡ chạy git stash drop hoặc clear cũng chưa mất hết. Bản ghi là một commit, và cho đến khi bộ thu gom rác xóa nó, có thể tìm lại trong số các đối tượng «mồ côi»:
git fsck --no-reflog | grep "dangling commit"
git show 8c1d2e3 # kiểm tra đúng là stash cần tìm
git stash apply 8c1d2e3
Nếu vừa xóa bản ghi bằng drop hoặc pop, hash được in ngay trong kết quả lệnh — dòng Dropped refs/stash@{0} (...). Chỉ cần truyền nó vào git stash apply.
Cấu trúc bên trong của stash
Hiểu cấu trúc giúp bớt lo sợ mất dữ liệu. Mỗi bản ghi stash là một commit thông thường, được trỏ tới bởi tham chiếu refs/stash. Commit này có hai cha: commit HEAD nơi bạn đang đứng, và một commit riêng chứa trạng thái index. Khi dùng -u, thêm cha thứ ba cho các tệp chưa theo dõi. Danh sách stash@{N} chỉ là nhật ký (reflog) của tham chiếu refs/stash.
Từ đó có vài hệ quả thực tế. Có thể đưa stash cho bất kỳ lệnh nào nhận commit: git diff stash@{0}, git show stash@{0}:src/auth.js (nội dung một tệp trong bản ghi), hoặc git checkout stash@{0} -- src/auth.js để lấy riêng một tệp mà không áp dụng cả bản ghi. Còn bản ghi đã xóa vẫn nằm trong cơ sở dữ liệu cho đến khi bộ thu gom rác dọn đi.
Stash và các lệnh Git khác
Một số lệnh tự biết cất tạm. git rebase --autostash và git pull --rebase --autostash trước khi bắt đầu sẽ cất thay đổi chưa commit, rồi trả lại khi xong. Có thể bật hành vi này thường trực bằng rebase.autoStash true và merge.autoStash true. Nếu khi trả về có xung đột, Git giữ bản ghi trong ngăn xếp stash thông thường và thông báo cho bạn.
Chuyển nhánh bằng git switch cũng không nhất thiết cần stash: nếu thay đổi của bạn không đụng tới các tệp khác nhau giữa hai nhánh, Git sẽ tự mang chúng theo. Stash cần khi Git báo «Your local changes would be overwritten by checkout», hoặc khi bạn không muốn đem chỉnh sửa sang nhánh khác.
Lỗi thường gặp
- Tệp mới «không được cất». Chúng chưa được theo dõi, cần
-u. - Dùng stash như kho lưu trữ dài hạn. Sau một tháng có mười bản ghi «WIP on main» không tên, và không ai nhớ trong đó có gì. Với công việc dài hạn hãy commit vào một nhánh riêng — ít nhất có thể đẩy lên máy chủ.
- Quên rằng bản ghi còn lại sau xung đột, rồi áp dụng lại lần nữa. Sau khi xử lý xung đột, luôn chạy
drop. - Tưởng stash đồng bộ với máy chủ. Nó chỉ nằm trong kho cục bộ,
git pushkhông gửi nó đi. Xóa thư mục dự án là mất stash. - Áp dụng nhầm bản ghi. Trước khi
pop, hãy xemgit stash listvàgit stash show -p.
Tóm lại
git stash là cách nhanh để giải phóng thư mục làm việc mà không mất thay đổi chưa commit: git stash push -u -m "mô tả" để cất, git stash pop để lấy lại. Dùng apply khi cần đệm an toàn, git stash list và show -p để không nhầm lẫn, và đừng biến stash thành kho chứa đồ — cho mọi việc dài hạn đã có commit và nhánh. Chuyển đổi giữa các công việc bằng stash được thực hành trong khóa học Git.
Câu hỏi thường gặp
Có áp dụng stash ở nhánh khác được không?
Được, stash không gắn với nhánh: chuyển sang nhánh cần thiết rồi chạy git stash pop hoặc apply. Nếu thay đổi không khớp, sẽ có xung đột thông thường.
git stash có giữ thay đổi trong index không?
Có, nhưng khi pop hoặc apply thông thường, mọi thứ quay về dạng thay đổi chưa chuẩn bị. Để khôi phục cả index, dùng git stash apply --index.
Có thể giữ bao nhiêu bản ghi stash?
Không có giới hạn cứng, nhưng bản ghi cũ hơn thời hạn lưu của reflog có thể bị xóa khi dọn rác. Thực tế nên giữ trong ngăn xếp vài bản ghi gần nhất.
Làm sao cất tạm chỉ một tệp?
Chỉ định đường dẫn sau dấu gạch ngang kép hoặc ngay sau push: git stash push -m mô-tả -- src/config.js. Chỉ tệp này được cất, các chỉnh sửa khác vẫn ở thư mục làm việc.
Stash khác commit tạm thế nào?
Commit nằm trong một nhánh, có thể đẩy lên máy chủ rồi sửa bằng amend hay rebase. Stash chỉ tồn tại cục bộ trong ngăn xếp riêng và phù hợp cho các lần chuyển việc ngắn.
Chủ đề này được giảng chi tiết kèm thực hành trong khóa học “Git с нуля до уверенного разработчика”.
Đến khóa học