ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

IDEA暂存和Git stash分不清?一文搞懂操作与区别

IDEA暂存和Git stash分不清?一文搞懂操作与区别 平时写代码最怕遇到什么不是需求改来改去而是你正改到一半产品经理突然跑过来说“先别写了线上有个紧急Bug你马上切分支修一下。”这时候你手头那堆半成品代码怎么办提交吧commit message 都不知道怎么写还得污染提交历史直接切分支吧Git 又不让你带脏工作区过去。于是你就站在了“暂存代码”这个十字路口。打开 IntelliJ IDEA菜单里能找到“暂存更改”打开终端git stash 也能干这件事。但有一个尴尬的现实我用 IDEA 两年一直以为“暂存”和 git stash 是一回事直到某次把未跟踪的新文件也顺手 stash 掉才猛然发现这两个入口背后隐藏的细节完全不一样。这篇文章就想把 IDEA 的暂存功能和 Git 的暂存功能放到台面上从操作路径、底层原理、适用场景、踩坑经验四个维度做一次真正意义上的对比让刚接触的同学不至于像我当初一样稀里糊涂。1. 先把概念掰扯清楚此“暂存”非彼“暂存”1.1 Git 里的“暂存”其实有两条线只要你在 Git 的世界里待过几天就一定听过“暂存区”Index / Staging Area这个词。git add把文件放进暂存区git commit把暂存区内容固化成本地提交git restore --staged再把它从暂存区撤出来。这也是 “暂存代码功能” 最常见的一种释义提交前的中间缓冲。IDEA 的 Changes 面板里就有一个专门的 Stage 按钮对应git add的图形操作。但标题里说的“暂存代码功能”在中文开发者嘴里通常指的是另一件事git stash。它把当前工作区里还没有提交的修改包括已经 add 的、还没 add 的整体打包存起来然后让工作区恢复成干净状态等你处理完别的事情再把这些修改重新取出来。Git 官方中文文档把 stash 翻译成“贮藏”但大家习惯叫“暂存”或“暂存更改”。于是问题来了你看到的“暂存”到底指哪一个这里必须插一句中文翻译的锅让很多人一上来就懵。IDEA 的中文界面里Stage 被翻译成“暂存”Stash Changes 被翻译成“暂存更改”两个菜单长得几乎像亲兄弟。我此前在某篇文章评论区看到有人问“为什么我点了暂存按钮代码还在工作区没被藏起来”底下答案五花八门其实他点的是 Stage 而不是 Stash。1.2 IDEA 里的“暂存”入口与中文翻译IDEA 里和“暂存”有关的入口大概有这么几个界面名称英文原文实际作用对应 Git 命令暂存按钮Changes 面板Stage把文件加入暂存区为提交做准备git add取消暂存Unstage把文件从暂存区移回工作区git restore --staged暂存更改主菜单 Git 下Stash Changes...把未提交的修改打包藏起来git stash push取消暂存更改主菜单 Git 下Unstash Changes...把之前藏起来的修改恢复出来git stash pop / apply看完这个表格你应该能明白IDEA 界面里凡是叫“暂存”的未必是你想要的操作。我刚用 IDEA 那会儿就想不通Git 菜单下那个“暂存更改”到底跟我 Changes 面板里的“暂存”按钮有什么区别直到我对着英文界面才意识到一个是 Stash一个是 Stage纯粹是翻译撞车。1.3 别混淆的还有 Shelve搁置功能IDEA 里除了 Stash Changes还有一个叫 Shelve Changes搁置更改的选项。这两个功能在界面上也是邻居实际操作感觉也差不多但底层逻辑完全不同Stash 走的是 Git 自带的 stash 机制会真正生成一个 stash 对象存在于你仓库的.git目录里Shelve 是 IDEA 自己的功能把改动打包成一个补丁文件放到 IDEA 的本地配置目录里和 Git 仓库本身没有任何关系。所以如果你在 IDEA 里 Shelve 了代码同事用命令行git stash list是看不到这条记录的因为你压根没有调用 Git 的 stash代码也没进入 Git 的存储体系。反之你在命令行 stash 的东西IDEA 的 Stash 列表里能看到因为它读的就是 Git 仓库自身的引用。一句话Shelve 更偏向“IDE 级别的临时收起来”Stash 更偏向“Git 仓库级别的临时存管”。平时单机开发、只有自己一台机器用 IDEAShelve 偶尔用用没问题但只要你需要在不同机器、不同终端、不同协作者之间搬移这份“半成品”就应该走 Git Stash。Shelve 是 JetBrains 官方在一些场景下偏爱推荐的功能但它的迁移性和可发现性远不如 Git stash。2. IDEA 图形界面的暂存适合什么场景2.1 Stash Changes 对话框里有哪些选项在 IDEA 主菜单找到 Git展开后点“Stash Changes...”会弹出一个对话框看起来很简单但里面每个选项都值得你多看一眼。第一个是 Message 输入框给你这次暂存写一句说明。很多人习惯直接空着但我强烈建议每次都写。原因很简单stash 在恢复列表里默认显示的是stash{0}这种编号如果你不写消息过两天根本分不清哪条是哪个分支上的东西。写一句“用户中心-改头像逻辑-未完成”之类的备注恢复的时候一眼就能认出来。第二个是 “Include untracked files” 复选框。这是 IDEA 暂存操作里最容易被忽略、也最影响结果的选项。Git 原生git stash默认只藏已跟踪文件的修改新创建的文件untracked files不在范围内IDEA 把这个选项默认放在对话框里勾上之后连那些“还没被 Git 跟踪的新文件”也会一起藏走。如果你不勾切分支之后那个新建但未提交的UserService.java仍然赖在工作区里不跟你走。第三个是新版 IDEA 里更细化的选项比如是否包含忽略文件、是否保留暂存区状态之类。对这些我想说一句除非你明确知道自己在做什么否则默认配置就够了。勾选太多东西恢复时的冲突概率会指数级上升。2.2 完整的 IDEA 暂存与恢复操作流程我们用一次典型的“临时插队修 Bug”场景走一遍完整流程。假设你正在feature/user-center分支上改代码改了一半还没有 commit。这时候收到消息需要立刻切到hotfix/login-timeout分支修复线上问题。第一步点击主菜单Git - Stash Changes...在弹出的对话框里写清楚 Message比如“用户中心头像功能开发中”然后根据你自己的需要决定要不要勾选“Include untracked files”。如果你这次的改动里包含新建的实体类、工具类等新文件建议勾上否则切到目标分支后这些新文件会原地不动可能干扰你修 Bug。点击 Create Stash。第二步IDEA 右下角会弹出一个气泡提示Changes 面板里的改动全部消失工作区变干净了。这时候放心切换分支git checkout hotfix/login-timeout不会遇到任何阻碍。第三步在 hotfix 分支上把 Bug 修完、提交、推送。修完再切回feature/user-center准备继续刚才的活。第四步主菜单Git - Unstash Changes...打开对话框后你会看到一个下拉列表里面是过往所有 stash 记录IDEA 会显示你填写的 Message方便区分。选中刚才那条下面有两个关键选项Pop 和 Apply。Pick Pop 的话恢复改动之后会自动删除那条 stash 记录Apply 则只恢复内容stash 记录保留方便你反复取用。如果是“恢复之后还要继续在这份代码上开发”的场景Pop 就够了如果只是临时看看某条 stash 里有什么代码用 Apply 更安全。第五步恢复之后如果工作区里有文件发生冲突比如目标分支上这个文件已经被别人改过了IDEA 会直接把文件标记为冲突状态界面右侧弹出 Merge 工具。在这里你可以一块一块地对比左侧、右侧、合并后的结果选完后点击 Apply 生效。处理完冲突回到 Changes 面板代码恢复到暂存之前的状态该继续开发继续开发。2.3 IDEA 方式最大的优点和隐藏坑IDEA 图形化暂存最大的优点我觉得不是“不用记命令”而是信息可视。每一条 stash 记录都带着 Message点击后能在侧边栏直接 diff 查看这次改动的内容。这让“找回半年前的某个临时改动”变得几乎零成本——你不需要记得 stash 编号只需要在列表里翻一翻看到疑似目标就点开对比一眼比命令行那一串stash{7}直观太多。但 IDEA 的暂存功能也有几个隐藏坑值得提。第一个坑是菜单位置在不同版本里飘忽不定。老版本里 Stash Changes 在VCS - Git菜单下新版本改成了顶部独立的Git菜单如果你用的是中文语言包不同版本翻译也不稳定我见过“暂存更改”“贮藏更改”“Stash 更改”三种翻译法。找不到按钮的时候别怀疑自己眼瞎直接搜Stash这个英文关键词搜索框会帮你定位。第二个坑是 IDEA 的 Stash 默认不一定包含 untracked 文件。前面说了对话框里有个复选框但这个选项不会记忆你的上一个选择每次操作都要重新看一遍。我就有过一次漏勾的情况一个新建的配置文件没跟着 stash 走切到目标分支后在旧逻辑下各种报错排查了半小时才意识到是那个新配置文件还留在工作区里把整个环境的加载路径搞乱了。第三个坑是快捷键。IDEA 默认没有给 Stash Changes 绑快捷键很多人就以为这个功能很底层、不安全于是转向命令行。其实在 Keymap 设置里搜“Stash”自己分配一个CtrlAltS之类的组合键用顺手之后效率极高完全不用纠结入口。3. 命令行 Git 暂存把控制权握在自己手里3.1 最常用的一套 stash 命令命令行方式适合两类人一类是已经习惯终端流看不得鼠标点来点去另一类是工作环境根本不给你装 IDEA比如你在服务器上直接改代码、直接在终端里操作。不管哪类先掌握下面这组最常用的命令就够应付大部分场景。# 把当前未提交的修改打包暂存message 里写清楚背景 git stash push -m 用户中心头像功能开发中 # 如果要连同未跟踪的新文件一起暂存 git stash push -u -m 用户中心头像功能开发中含新增文件 # 查看所有暂存记录 git stash list # 查看某条暂存的具体改动内容 git stash show -p stash{0} # 恢复最近一条暂存并删除该暂存记录 git stash pop # 恢复某条指定暂存但保留暂存记录 git stash apply stash{1} # 删除某条暂存记录 git stash drop stash{0} # 清空所有暂存记录 git stash clear # 从某条 stash 创建新分支并应用改动 git stash branch fix-xxx stash{0}如果只是临时避开冲突git stash加git stash pop两下就够但项目稍微复杂一点多分支并行开发时list、show、apply stash{n}这些带定位能力的命令才是真正的主角。3.2 stash 默认行为与几个容易忽略的参数git stash默认只暂存已被 Git 跟踪的文件的修改。注意是“跟踪”而不是“已提交”。也就是说一个文件只要你曾经 add 过Git 就认识它之后你对它做的修改、删除、重命名都会被 stash 一并收走。但一个全新创建、从未 add 过的文件默认会被留在工作区。这个设计其实是为了安全。Git 不想在你看不见的地方偷偷搬走你刚建的文件万一你不记得 stash 过它们文件就相当于消失了。但从实际经验看这个“安全”恰恰是最容易踩的坑——我以为 stash 已经把工作区清空了切到别的分支却发现自己新建的工具类还在那儿躺着造成环境、编译路径上的各种错乱。想搞定 untracked 文件有两个参数# 连同未跟踪文件一并暂存 git stash push -u # 连被 .gitignore 忽略的文件也一并暂存慎用 git stash push -a还有一个容易被忽略的--keep-index参数。默认情况下 stash 会把已经 add 到暂存区的东西也一起藏走但如果你只想要“保留暂存区、只藏工作区里未暂存的那部分修改”可以用git stash push --keep-index这在做“只提交部分改动”的场景下非常有用你已经把 A 文件 add 了B 文件还没 add你想让 A 留在暂存区不动把 B 先藏起来那么--keep-index就是你要的选项。交互式 stash 也是命令行才有的高级玩法git stash push -p这个命令会进入交互界面让你按 hunk 块选择“哪些改动要暂存、哪些不暂存”。如果你改了五个文件但这次只想把其中两个文件的部分改动藏起来这个-p比任何图形界面都灵活。3.3 命令行适合谁命令行 stash 真正拉开差距的不是单次操作而是可编程、可批量、可嵌入脚本。我自己的一个实际例子周期性迭代的项目里每周五下班前要把手头所有未提交改动安全存放早上来再恢复。这个操作完全可以写成一个 shell 片段#!/bin/bash # 一键暂存当前分支的所有未提交改动按分支名日期命名 branch$(git rev-parse --abbrev-ref HEAD) git stash push -u -m autostash: ${branch} $(date %Y%m%d)随后你只要执行这个脚本无论手头改动多乱仓库都会自动恢复成干净状态。服务器上部署、定时任务里处理代码变更都是命令行 stash 大显身手的地方。4. 关键差异对比与选择建议4.1 一张表看懂核心差异我不喜欢那种罗列上百行的功能对比真正影响选择的核心差异用一张表就能说清楚对比维度IDEA 图形化暂存Git 命令行 stash操作门槛鼠标点击不需要记命令需要记命令和参数有学习成本查看内容列表里有 Message可点开 diff靠git stash show -p查看相对原始是否包含未跟踪文件对话框里勾选项默认不勾需要手动加-u默认不含精细化控制较弱整体暂存整体恢复-p按 hunk 挑选--keep-index等高级控制批量/脚本化不支持完全支持跨终端/服务器使用不支持必须打开 IDE直接在任意终端执行数据记录存放就是 Git stash 对象和命令行互通就是 Git stash 对象冲突处理IDEA 自带可视化 Merge 工具体验好命令行提示冲突文件需要手改或用其他工具查找历史记录界面有 Stashes 列表视觉直观git stash list一排编号不太友好注意一个关键结论IDEA 的暂存和命令行的 stash本质上操作的是同一个 Git stash 对象。两者不是两套独立系统而是同一个仓库数据在不同界面下的两种操作方式。所以你完全可以在 IDEA 里创建 stash在命令行里恢复反之亦然。前提是操作完 IDE 需要刷新一下通常在外部变更后会自动弹窗提示。4.2 我的场景化选择准则做了几年开发我对“该用哪个”的判断已经不看技术只看场景。这里直接把我自己的选择逻辑讲给你。如果你正坐在 IDEA 里改动比较集中就是一次普通的“切分支前藏一下代码”那用 IDEA 的 Stash Changes 最快。整个过程不出 IDE还有可视化冲突解决兜底何苦去背命令如果你的工作流里带着自动化、定时任务、跨机器同步比如你在家里电脑和公司电脑之间来回切换或者需要在脚本里把暂存和恢复做成一个步骤那必须用命令行因为 IDEA 的图形按钮没法塞进脚本里。如果你经常遇到“只暂存一半改动”的需求比如改到一半突然想重新整理工作计划只把 A 部分收起来、B 部分继续留在工作区那命令行配合-p和--keep-index是唯一不别扭的方案。IDEA 虽然也有将文件加入 Changelist 之类的变通办法但论原生顺手程度还是不行。如果你属于团队协作频繁、git pull 经常带出冲突的人我建议优先用 IDEA 暂存并恢复。因为恢复时如果有冲突IDEA 会直接把三方合并界面弹到眼前左边是你 stash 的改动右边是当前分支的改动下面是可以手动编辑的合并结果。对一个刚工作一两年的同学来说这个可视化流程比在终端面对分隔符要友好得多。还有一个很难挂在教程里但实际非常影响体验的因素紧急程度。如果是线上故障时间以分钟计我反而建议直接git stash push -u -m 紧急修复然后git checkout因为命令行三秒内能完成操作不需要在 IDEA 里打开菜单、等对话框、再调整选项。图形工具在高压场景下反而会让你手忙脚乱。4.3 一个容易忽略的问题stash 不属于某个分支这是 Git stash 设计里最有意思、也最容易踩坑的一个点stash 是仓库级的不挂在任何分支名下。你在feature/a分支 stash 的东西切到master分支再git stash pop它一样能恢复。Git 并不会因为分支不同就拦着你。听上去很方便实际上是双刃剑。如果你 stash 的内容和当前分支上已有的改动发生重叠pop 时就可能产生冲突。我在团队里见过这样一种事故同事在feature/a上 stash 了一段公共工具类的修改忘了写消息两天后他在master分支上清理 stash 列表看到一条不知道哪来的记录随手 pop结果和master上别人提交的同类修改撞了个满怀打开文件一看满地冲突标记。所以使用 stash 时必须把“它属于仓库不属于分支”这件事刻在脑子里。养成习惯每次 pop 之前先用git stash list确认目标并且 pop 之后立刻看一眼git status确认没有恢复出不该出现的东西。5. 常见问题与排查技巧实录5.1 stash pop 时冲突的完整处理过程stash 恢复时遇到冲突绝大多数人第一反应是“这 stash 废了”其实完全不是。冲突只是说明当前分支和 stash 里的改动改到了同一个文件解决掉就完事。命令行流程是这样。执行git stash pop终端会提示哪些文件 conflict同时工作区里这些文件会被 Git 标记为 conflicted。先git status看看冲突文件清单然后打开文件里面一行行写着 Updated upstream、、 Stashed changes之类的分隔符手动把代码改成你想要的样子删掉分隔符。全部处理完执行git add标记已解决再确认没有其他冲突文件通常 Git 会自动完成 stash pop 的收尾工作那条记录也会被自动删除。在 IDEA 里更简单执行 Unstash Changes 后如果弹出冲突提示双击冲突文件IDEA 打开三方 Merge 窗口。左侧是你的 stash 版本中间是最终合并区右侧是当前分支版本。把想要的改动一块一块点过去完成后点 Apply。不用碰任何分隔符。这里有个经验不要图省事用git checkout --theirs或--ours这类命令无脑覆盖。stash 里的改动和当前分支的改动都可能是你想要的内容无脑覆盖十有八九会造成代码丢失。5.2 stash 记录丢了怎么找回来我见过不少“误清 stash”的同学第一反应是“完了代码没了”。实际上 stash 记录在没被 Git 垃圾回收之前大概率救得回来。Git 的 stash 机制其实是一个特殊的引用refs/stash。在 stash 对象还没有被git stash clear或git stash drop删除的时候这个引用指向上层提交。即使你 drop 了stash 提交对象也不会立刻从 Git 仓库中消失它只是变成了“不可达对象”在 Git 执行 gc垃圾回收之前它还会在.git/objects目录里躺一段时间。找回的通用法门是git fsck --no-reflogs --unreachable输出里会有一堆unreachable commit ...你没办法直接从这些哈希值看出哪条是你要的 stash。更实用的方式是利用 refloggit reflog show --all | grep -i stash或者直接看 stash 的 refloggit log --oneline -g refs/stash如果你在时间窗口内翻到了疑似目标用git show sha查看内容确认无误后可以这样恢复成新分支git checkout -b recovered-stash sha就能看到之前 stash 的完整内容了。注意这个办法有时效性如果项目仓库自动执行了 gc或者隔了很长时间不可达对象可能已经被清理那就真的找不回了。5.3 误删 stash、误 pop 之后的补救和 5.2 类似如果你不小心git stash drop了一条记录发现自己还没准备好同样可以尝试用 reflog 找回。但如果已经git stash pop完成感觉“恢复出来的代码不对”想撤销操作就复杂一些。一种常见请求是“我刚 pop 了发现 pop 错分支了想退回 pop 之前的状态。”严格说 Git 没有直接一条命令能撤销 pop但思路是清楚的pop 的本质是 apply dropapply 产生的改动都在工作区里你想“撤销 apply”的话可以用git stash apply stash{n}对应内容反方向操作实际操作中你只需要在错误分支上把 pop 出来的改动回退到上一个正常状态再在正确分支重新 apply 那条 stash。如果你的 stash 记录在 pop 时因为冲突而没有自动 drop那更好办直接git stash drop之前重新处理。如果记录已经 drop 了工作区又乱成一团优先用 5.2 的 fsck 大法找回 stash 对象再想办法把工作区清理干净。说到底养成“pop 之前先确认分支、pop 之后立刻 git status”的习惯比任何补救手段都管用。5.4 IDEA 与命令行混用的注意事项现实中很少有人只用一种方式大部分人都是 IDEA 写代码、终端偶尔敲命令两边混着用。Git stash 是仓库级功能所以 IDEA 和命令行操作的是同一条记录这里有三条注意点。第一IDEA 对“外部修改”的感知是滞后的。你在终端里git stash push之后回到 IDEA如果 IDEA 没有自动弹“外部更改”的刷新提示要手动按CtrlAltO或点击右上角刷新按钮否则界面上的 Changes 面板还停留在旧状态会误导你。有一次我在终端 stash 之后回到 IDEA看到 Changes 面板里改动都还在还以为是 stash 失效差点把代码清掉其实就是没刷新。第二别用 IDEA 的 Undo 去撤销 stash。IDEA 没有为 stash 提供完善的 Undo 历史CtrlZ 撤不回收支的 Stash 操作。一旦在 IDEA 里误删了一条 stash想找回还是要回到命令行用 5.2 的办法。所以我自己的习惯是在 IDEA 里创建 stash 之前先在终端里看一眼git stash list心里有数。第三尽量不要在同一个 stash 上反复做 IDEA 的 Apply 且不删记录。某些场景下IDEA 对 stash 恢复后的文件状态跟踪会和命令行预期不一样导致你明明操作了却出现“代码没恢复”“代码重复出现”之类的错觉。遇到这种情况烦躁之前先打开终端跑一遍git stash list和git status以命令行的输出为准界面显示的异常状态多半只是没刷新。## 6. 我的最终建议不用二选一而是分场景切换 写了这么多最后说说我自己现在的使用习惯。在 IDE 里日常开发小步快跑、频繁切分支的场景我选择 IDEA 的 Stash Changes因为它把“暂存”“恢复”“冲突解决”串成了一个完整闭环而且恢复时的三方 Merge 更符合人脑思维。但凡牵扯到批量操作、跨机器同步、服务器排查、脚本自动化我毫不犹豫切到命令行用 git stash push -u -m ... 和 git stash pop 配合脚本完成。 另外两件小事对提升 stash 体验特别有帮助。一是每一条 stash 都写清楚 Message不要偷懒。二是 stash 数量尽量不要超过五条超过之后列表会变得非常难维护定期清理无用的 stash 记录既省心又避免误操作。 最后一个工作小技巧如果你有“临下班 stash 代码、第二天早上恢复”的习惯与其在 stash 和 pop 之间反复横跳不如直接给 IDEA 的 Stash Changes 和 Unstash Changes 各配一个顺手的快捷键并选好用 Apply 而不是 Pop。原因无他Apply 不删除 stash 记录万一恢复后发现不对劲你还能退回原样重新处理。这一个小习惯帮我避掉了很多次“代码恢复后发现问题但原 stash 已消失”的尴尬。
返回列表