Git stash 保存临时工作:未跟踪文件、apply 与 pop 各自留下什么
临时切走处理另一个问题前,执行 stash 后发现新建文件还在原地,通常不是保存失败。默认范围主要是已跟踪文件的修改,新建的未跟踪文件需要明确选择是否纳入。保存之前先看状态,才能知道工作现场究竟包含哪些部分。
恢复动作也有不同承诺:apply 尝试应用内容并保留条目,pop 在成功应用后移除条目。如果发生冲突,pop 会把条目留下,让你处理当前恢复结果。不能把 pop 理解为“无论怎样都删除”,也不能在冲突后反复运行它期待自动解决。
用三个独立仓库观察三种结果
下面保存为 demo.sh,用 Bash 和 Git 运行。所有操作都落在新建临时目录,使用演示提交身份。第一组检查默认保存范围及 apply 保留;第二组检查 -u 与成功 pop;第三组制造同一行修改冲突,确认失败退出与条目保留。
脚本把较长的 Git 提示保存到临时日志,只输出断言通过后的结论。冲突仓库故意保留在临时目录,没有执行清理或丢弃现场。读者不需要把示例改到自己的项目里,就能先看清这些命令的边界。
AI概念示意图:以抽象物件说明本文主题,不代表真实界面或运行结果。
set -euo pipefail
stash_demo=$(mktemp -d)
make_repo() {
git init -q -b main "$stash_demo/$1"
cd "$stash_demo/$1"
git config user.name Demo
git config user.email demo@example.invalid
git config commit.gpgsign false
printf 'base\n' > tracked.txt
printf 'ignored.log\n' > .gitignore
git add .
git commit -qm base
}
make_repo default
printf 'draft\n' > tracked.txt
printf 'new\n' > new.txt
printf 'local\n' > ignored.log
git stash push -qm tracked-only
test "$(cat tracked.txt)" = base
test -f new.txt && test -f ignored.log
git stash apply -q > "$stash_demo/apply.log" 2>&1
test "$(cat tracked.txt)" = draft
test "$(git stash list | wc -l)" -eq 1
printf 'apply: tracked restored; stash kept; new file never left\n'
make_repo untracked
printf 'draft\n' > tracked.txt
printf 'new\n' > new.txt
printf 'local\n' > ignored.log
git stash push -u -qm include-new
test ! -e new.txt && test -f ignored.log
git stash pop -q > "$stash_demo/pop.log" 2>&1
test "$(cat new.txt)" = new
test "$(git stash list | wc -l)" -eq 0
printf 'pop success: untracked restored; ignored stayed; stash removed\n'
make_repo conflict
printf 'draft\n' > tracked.txt
git stash push -qm conflicting-draft
printf 'current\n' > tracked.txt
git add tracked.txt
git commit -qm current
if git stash pop -q > "$stash_demo/conflict.log" 2>&1; then
printf 'unexpected pop success\n' >&2
exit 1
fi
test "$(git stash list | wc -l)" -eq 1
test "$(git status --porcelain tracked.txt)" = 'UU tracked.txt'
printf 'pop conflict: unresolved file; stash kept\n'先核对范围,再决定怎样恢复
第一行确认默认 stash 没有带走 new.txt,apply 恢复已跟踪修改后仍保留一个条目。第二行确认 -u 带走并恢复了未跟踪文件,而 ignored.log 一直留在工作区。-u 并不包括被忽略文件,不能把它当作整个目录的完整快照。
更宽的 -a 还会纳入忽略文件,可能涉及体积很大的构建产物或本地材料,因此应先检查清单。本例没有执行这个选项。实际操作也不要只因为想让目录看起来干净,就扩大保存范围;先判断哪些文件需要跟随这次临时工作。
apply 适合希望保留一份恢复入口的阶段。确认内容正确后,再明确处理那条记录。pop 更像“恢复成功就消费这份条目”,但它不是整体事务保证;发生冲突时,工作区可能已经出现部分恢复内容和冲突标记,需要检查当前状态。
冲突后查看现场,不重复套用
第三行说明 pop 返回失败,tracked.txt 为未合并状态,stash 仍有一条。此时应阅读差异并解决冲突,而不是再 pop 一次。同一份内容重复应用会增加判断难度;等恢复结果确认完整,再决定保留或移除对应条目。
当暂存区原来也有独立安排时,恢复文件内容不一定等于恢复原有暂存边界。需要恢复索引状态可研究 --index,并用自己的样本测试;它也可能因冲突而失败。本文只检查工作文件与条目数量,没有承诺暂存状态完全还原。
在日常列表里使用带含义的消息,记录正在做什么和适用分支,比只看时间顺序更容易恢复。stash 编号会随着新增或移除改变,重要工作不宜长期只靠一个位置编号识别,更不应把临时条目当成唯一的持久备份。
最后,把保存与恢复都做成可观察步骤:保存前查看状态,保存后确认条目和遗留文件,恢复后看差异与未合并项。三个检查点能分别发现范围遗漏、错误工作目录和恢复冲突,让“暂时放一下”真正可以放心接着做。


