Về blog

git stash: chỉ một file, không đụng đến phần còn lại

21 tháng 9, 2026

Một code review đổ xuống trong khi ba file đang sửa dở và chỉ một trong ba được phép nhúc nhích. Stash hết mọi thứ rồi gõ lại hai file kia — cái nghi thức mà chẳng ai nhớ mong.

TL;DR: git stash push -m "lý do" -- <path> stash đúng các đường dẫn được nêu và để phần còn lại của cây bẩn nằm yên. Lấy lại thay đổi bằng git stash pop (hoặc rút một file riêng từ stash bất kỳ bằng git restore --source stash@{0} -- <path>). Dạng pathspec có từ git 2.13, phát hành tháng 5/2017 — mọi toolchain tám năm gần đây đều có.

git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop

Stash cái file, không phải cả cái cây.

Làm sao stash chỉ một file trong git?

git stash trơn quét sạch cả cây làm việc — mọi thay đổi được theo dõi vào stash, tất cả trở về HEAD. Đó là hình dạng sai khi cây bẩn lẫn lộn: một file sẵn sàng giao đi, phần còn lại thành thật là dở dang. Lời giải là động từ push kèm pathspec, theo tài liệu git-stash:

# trước: ba file bẩn
$ git status --short
 M app.conf
 M notes.md
 M main.py

# chỉ stash app.conf
$ git stash push -m "app.conf prod tweak" -- app.conf
Saved working directory and index state On main: app.conf prod tweak

$ git status --short
 M notes.md
 M main.py

app.conf trở về trạng thái đã commit và nằm trong stash@{0}; notes.mdmain.py chẳng nhúc nhích. Thông điệp -m không bắt buộc nhưng đáng ba giây ngay khi git stash list có hơn một entry — stash tên “WIP” già nhanh lắm.

Hai chi tiết đáng nhớ:

  • Pathspec đứng sau --. Mọi thứ sau gạch đôi là đường dẫn, không phải cờ. Cùng quy ước với git checkout -- <path> và không phải để trang trí: bản cũ có lúc git stash push -- main.pygit stash push main.py khác nhau, khi đường dẫn trần bị đọc nhầm.
  • File untracked cần -u. File mới tinh không có lịch sử theo dõi để stash, nên push trơn bỏ qua nó. git stash push -u -- newfile.py sẽ kèm theo; -a đi xa hơn, quét cả file bị bỏ qua.

Làm sao stash một phần của một file?

Khi chính file đó đang dở — ba hunk ố, một hunk mắc cỡ — luồng tương tác xẻ nó ra:

git stash -p            # hoặc: git stash push -p

Git đi qua từng hunk và hỏi Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. Trả lời y cho hunk vào stash, n cho hunk ở lại. Kết quả là một stash chỉ chứa phần được duyệt, phần còn lại của file vẫn bẩn trên cây. Cùng cơ chế từng hunk ấy chạy git add -p, nên phản xạ chuyển nhà nguyên vẹn.

git stash push -- <path>git stash -p
Hạtcả filetừng hunk
Tốc độmột lệnh, script đượctương tác, hunk từng cái
Lặp lại trong CIcó (đường dẫn làm đối số)không — cần người đứng ở prompt
Hợp nhất cho“file này, phần còn lại không”“thay đổi này, thay đổi kia thì không”
Từgit 2.13 (5/2017)có sẵn từ lâu

Quy tắc thực chiến từ chính mô hình của tài liệu: ranh giới là file thì pathspec, ranh giới bên trong file thì -p.

Làm sao lấy lại một file đã stash?

git stash pop khôi phục stash@{0} và xoá entry. Đường chính — nhưng pop là tất-cả-hoặc-không-cái-gì theo từng stash, và đụng conflict thì pop dừng lại, entry vẫn giữ. Hai lựa chọn mảnh hơn:

# 1. áp dụng mà không xoá (lặp lại an toàn)
git stash apply stash@{0}

# 2. rút MỘT file từ stash, entry vẫn đứng
git restore --source stash@{0} -- app.conf
# cách viết trước 2.23 của cùng thao tác:
git checkout stash@{0} -- app.conf

Dạng restore/checkout trả lời tình huống “stash chung ba thay đổi, giờ chỉ cần một” — nó copy nội dung stash của đường dẫn đó sang cây làm việc và để stash@{0} vẫn đứng. Lưu ý: nó ghi đè bản trên cây làm việc; nếu các sửa đổi hiện tại trên đường dẫn đó quan trọng, diff trước:

git diff stash@{0} -- app.conf

Stash được giữ như commit thật trên một ngăn xếp kiểu reflog — vì thế stash@{0} nhận mọi cú pháp commit-ish, và stash lỡ tay bỏ cũng nhặt được từ reflog cho tới khi trình dọn rác ăn nó.

Khi nào stash là công cụ sai?

Stash là giấy nháp, không phải branch: không tên trong git branch, không review, mặc định không hiện diff, và các entry chồng chất trong im lặng đến lúc quên. Nếu công việc đang làm phải sống sót qua các lần đổi ngữ cảnh giữa máy và giữa ngày, commit lên branch — lịch sử gắn kèm hơn hẳn đống không tên. Khi những commit gửi xe ấy cần hạ cánh chỗ khác, git cherry-pick chở giúp. Còn mục tiêu là hoàn tác chứ không phải gửi xe, thì cây quyết định ở hoàn tác commit cuối áp dụng.

Sổ tay trung thực về các kiểu hỏng, tất cả từ hành vi được tài liệu hoá:

Triệu chứngNguyên nhânCách chữa
File mất tích sau stashđường dẫn untracked bị bỏ quagit stash push -u -- <path>
error: Your local changes ... would be overwritten khi popđường dẫn đổi từ lúc stashcommit hoặc stash trạng thái mới, rồi pop
Thay đổi vừa lấy biến mấtpop va conflict và dừnggiải quyết, rồi git stash apply
“Stash nào nhỉ?”đống stash không tênthông điệp -m, luôn luôn

Trang release note tháng 5/2017 cộng pathspec vào git stash push nay tám tuổi, còn phần lớn trí nhớ cơ bắp của git stash thì già hơn. Đáng học lại một lần: quét cả cây giờ là trường hợp đặc biệt, không phải mặc định.

FAQ

Làm sao stash chỉ một file trong git?

git stash push -m "ghi chú" -- path/to/file — dạng pathspec chỉ stash đúng các đường dẫn được nêu và giữ nguyên mọi file bẩn còn lại. Cần git 2.13 trở lên (tháng 5/2017).

Làm sao lấy một file riêng từ stash?

git restore --source stash@{0} -- path/to/file (hoặc dạng cũ git checkout stash@{0} -- path/to/file). Nó copy phiên bản đã stash vào cây làm việc mà không xoá entry của stash.

Vì sao git stash bỏ qua file mới của tôi?

File chưa được theo dõi (untracked) không thuộc stash thường. Thêm -u để bao gồm: git stash push -u -- path/to/file. File bị bỏ qua (ignored) cần -a.

— mrsaynothing

— mrsaynothing

Ghi chú thực địa về AI, Linux và self-hosting.

Nhận how-to tiếp theo qua email

Một email mỗi bài viết. Sửa xong rồi đi tiếp.

self-hosted · không bên thứ ba · hủy đăng ký một cú bấm

cái này là gì?

Ghi chú thực địa #1, trang do agent vận hành: Máy ship. Tôi duyệt.

Thích kiểu viết này? Tôi build như vậy để kiếm sống. thuê tôi