3、Git进阶操作:文件撤销、版本回退、删除文件、移动文件、忽略文件
好,咱们继续往前走。上一章我们把Git的基本操作捋了一遍,add、commit、push这些你应该已经玩熟了。但说实话,日常开发中真正让你头疼的,往往是那些「哎呀手滑了」、「完了改错了」、「这个文件不该提交」的瞬间。
这一章,我就带你把这些「后悔药」吃明白。说白了,Git的进阶操作就是教你如何优雅地犯错,再优雅地收场。
本章核心:学会在Git中撤销、回退、删除、移动和忽略文件,掌握这些操作后,你基本能应对90%的日常场景。
3.1 文件撤销:把改错的东西变回去
先聊聊文件撤销。这可能是你用得最频繁的操作之一。你想想看,写代码哪有不手滑的?
我个人习惯把撤销分成两种情况:还没add的和已经add的。这两种的处理方式完全不同。
3.1.1 撤销工作区的修改(还没add)
假设你改了一个文件,改到一半发现方向错了。这时候文件还在工作区,没执行过git add。怎么办?
# 撤销单个文件的修改
git checkout -- filename
# 或者用新版Git的推荐写法
git restore filename
嗯,这里要注意。git checkout -- filename这个命令,会把文件恢复到最近一次git commit时的状态。你做的所有修改都会丢失,所以用之前一定要确认——你真的不想要这些改动了。
⚠️ 警告:git checkout -- 会直接覆盖文件,无法撤销。如果你只是不确定要不要改,建议先 git stash 暂存起来,给自己留条后路。
3.1.2 撤销暂存区的修改(已经add了)
如果你已经git add了,但还没commit,这时候想撤销怎么办?
# 把文件从暂存区移回工作区
git reset HEAD filename
# 新版Git写法
git restore --staged filename
这个操作只是把文件「请出」暂存区,文件内容不会丢失。它还在工作区里等着你重新考虑。
我曾经有一次在项目上线前,不小心把一个调试用的配置文件给add了。还好及时发现,用git reset HEAD把它撤了回来。你想想看,要是那个文件被commit甚至push上去,后果不堪设想。
3.2 版本回退:坐时光机回到过去
版本回退,说白了就是让整个项目回到某个历史状态。这个功能在Git里做得非常优雅。
3.2.1 查看历史版本
回退之前,你得先知道有哪些版本可以回。用git log查看提交历史:
# 查看简洁版历史
git log --oneline
# 查看带图形的历史
git log --oneline --graph --all
输出大概长这样:
a1b2c3d (HEAD -> main) 修复登录bug
e4f5g6h 新增用户注册功能
i7j8k9l 初始化项目
每个commit前面那串字符(比如a1b2c3d)就是版本号。记住它,回退就靠它了。
3.2.2 两种回退方式:reset 和 revert
这里有个重要的概念区分。我刚开始用Git时也搞混过,所以特意拿出来讲。
| 操作 | 命令 | 特点 | 适用场景 |
|---|---|---|---|
| 硬回退 | git reset --hard HEAD~1 |
直接删除历史,回退后的commit会消失 | 本地分支,还没push |
| 软回退 | git reset --soft HEAD~1 |
保留修改内容,只移动HEAD指针 | 想重新commit |
| 安全回退 | git revert HEAD |
生成一个新的commit来抵消旧commit | 已经push到远程的分支 |
# 回退到上一个版本(硬回退)
git reset --hard HEAD~1
# 回退到指定版本
git reset --hard a1b2c3d
# 安全回退(生成新commit)
git revert HEAD
💡 我的建议:如果分支已经push到远程了,千万别用 git reset --hard 然后强行push。团队其他人会恨你的。用 git revert 更体面。
为什么会这样?因为git reset会改写历史,而git revert是在历史后面追加。你想想看,如果别人已经基于你的commit做了开发,你突然把历史删了,他那边会乱成一锅粥。
3.3 删除文件:不只是删掉那么简单
删除文件看起来简单,但在Git里,你得告诉它「我是故意删的」。
3.3.1 用 git rm 删除
# 删除文件并自动记录到暂存区
git rm filename
# 删除整个目录
git rm -r directory/
这个命令做了两件事:一是从磁盘上删除文件,二是把这次删除操作记录到暂存区。下次commit时,Git就知道这个文件被移除了。
如果你只是手动在文件管理器里删了文件,Git会检测到「未跟踪的变化」。这时候你需要:
# 手动删除后,告诉Git确认删除
git rm filename
# 或者用 add 命令也能识别删除
git add -u
3.3.2 只从Git中删除,保留本地文件
有时候你不想让Git继续跟踪某个文件,但文件本身还想留着用。比如一些配置文件,你提交过一次示例后,后续不想再跟踪它的变化。
# 从Git中移除跟踪,但保留本地文件
git rm --cached filename
这个操作很实用。我记得有一次,项目里有个config.local.js文件,每个人本地配置都不一样。我把它提交过一次作为模板,然后用git rm --cached取消跟踪,再添加到.gitignore里。这样大家各自改自己的,互不影响。
3.4 移动文件:改名改位置
移动文件在Git里其实是个组合操作——先复制再删除。但Git贴心地提供了git mv命令,一步到位。
# 重命名文件
git mv oldname.txt newname.txt
# 移动文件到其他目录
git mv file.txt src/utils/
这个命令等价于:
mv oldname.txt newname.txt
git rm oldname.txt
git add newname.txt
我个人习惯直接用git mv,省事。但如果你用IDE的重命名功能,IDE会自动处理好这些,你只需要记得git add一下就行。
注意:Git跟踪的是文件内容,不是文件名。所以即使你改了文件名,Git也能通过内容相似度识别出「这是同一个文件」。用 git log --follow filename 可以查看文件的重命名历史。
3.5 忽略文件:.gitignore 的正确用法
最后聊聊忽略文件。这个太重要了,几乎每个项目都要用。
你肯定不想把node_modules、.env、编译产物这些文件提交到Git仓库里。这时候就需要.gitignore文件出场了。
3.5.1 .gitignore 的基本规则
# 忽略所有 .log 文件
*.log
# 但保留 important.log
!important.log
# 忽略 node_modules 目录
node_modules/
# 忽略 build 目录下的所有内容
build/
# 忽略以 temp 开头的文件
temp*
# 忽略 .env 文件(常见于环境配置)
.env
规则其实很简单:
*匹配任意多个字符?匹配单个字符!取反,表示不忽略/在末尾表示目录
3.5.2 全局忽略配置
有些文件是跟具体项目无关的,比如.DS_Store(macOS的目录配置文件)、Thumbs.db(Windows的缩略图缓存)。这些可以配置到全局忽略中:
# 设置全局 .gitignore
git config --global core.excludesfile ~/.gitignore_global
然后在~/.gitignore_global里写上:
.DS_Store
Thumbs.db
*.swp
*.swo
💡 避坑指南:我曾经遇到过一个问题——明明在 .gitignore 里写了忽略规则,但文件还是被跟踪了。后来发现,是因为这个文件之前已经被 git add 过。.gitignore 只对「未跟踪」的文件有效。如果文件已经被跟踪,你需要先用 git rm --cached 取消跟踪。
3.5.3 检查忽略规则是否生效
# 查看哪些文件会被忽略
git status --ignored
# 检查某个文件是否被忽略
git check-ignore -v filename
这两个命令在排查问题时特别有用。有时候规则写复杂了,自己都搞不清到底忽略了哪些文件。
小结一下
这一章的内容其实就围绕一个核心思想:Git给了你犯错的权利,也给了你改正的机会。文件撤销让你能反悔未提交的修改,版本回退让你能回到过去,删除和移动文件让你能整理项目结构,忽略文件让你能保持仓库干净。
这些操作单独看都不难,但组合起来就能应对绝大多数日常场景。你只要记住:git restore管撤销,git reset管本地回退,git revert管远程回退,.gitignore管眼不见心不烦。
嗯,差不多就这些。下一章我们会聊分支操作——那才是Git真正强大的地方。