先记住三个区域
Git 的很多“撤销”问题,本质是没搞清东西现在在哪个区域:
| 区域 | 说明 | 常用命令 |
|---|---|---|
| 工作区 | 你正在编辑的文件 | git status、git diff |
| 暂存区 | 已 add、等待提交的内容 |
git diff --staged |
| 本地仓库 | 已 commit 的历史 |
git log |
| 远程仓库 | 推送到服务器上的历史 | git push / git fetch |
撤销之前先执行 git status,看清楚改动处在哪一层,再选命令。上来就 reset --hard,是新手丢代码的头号原因。
HEAD 指向当前分支的最新提交,分支本身只是一个指向提交的指针。git commit 会移动当前分支指针,git switch 会移动 HEAD,理解这一点,reset 的三种模式就不难记了。
reset --soft 只移动指针,暂存区和工作区都不动;--mixed 是默认值,还会重置暂存区;--hard 三处一起重置。所以“想把提交拆成两次”用 --soft,“想重新 add”用 --mixed,“想彻底丢掉”才用 --hard。
提交之前养成两个习惯:先 git diff 看未暂存的改动,再 git diff --staged 看已暂存的内容。调试用的 System.out、注释掉的代码、临时改的配置,都能在这一步拦下来。
日常五条命令
git status # 看当前状态
git add src/main/java/... # 把指定文件放入暂存区
git commit -m "fix: 修正分页参数" # 提交到本地仓库
git pull --rebase origin main # 拉取远程并变基
git push origin main # 推送到远程
提交信息建议用“类型: 描述”的格式,比如 feat:、fix:、docs:、refactor:。半年后回头看历史,能一眼看出每次改动的目的,比一堆“update”有用得多。
撤销:先判断错在哪一步
| 情况 | 命令 | 结果 |
|---|---|---|
| 工作区改乱了,还没 add | git restore 文件名 |
丢弃该文件的修改 |
| 已 add,想撤出暂存区 | git restore --staged 文件名 |
改动还在工作区 |
| 已 commit,没 push | git reset --soft HEAD~1 |
撤掉提交,改动留在暂存区 |
| 已 commit,没 push,想全撤 | git reset --hard HEAD~1 |
提交和改动一起丢,慎用 |
| 已 push 到公共分支 | git revert 提交号 |
生成一个反向提交,历史保留 |
| 手头改动想临时放一边 | git stash / git stash pop |
工作区干净,稍后恢复 |
原则是:只影响自己的本地历史可以用 reset,已经推给别人的历史一律用 revert。公共分支被 reset 之后强推,其他人的本地分支会直接错乱。
分支与合并
git switch -c feature/login # 新建并切换分支(Git 2.23+)
git switch main # 切回主分支
git merge feature/login # 把功能分支合进主分支
git branch -d feature/login # 删除已合并的分支
老教程里的 git checkout -b 也能用,switch 只是把“切分支”和“恢复文件”两个职责拆开了,更不容易误操作。合并方式上,merge 会保留分支结构,rebase 会把提交接到目标分支后面、历史更线性。个人分支合并前可以 rebase 整理,公共分支不要 rebase。
常见坑
1. reset --hard 之后代码没了
--hard 会同时丢弃工作区和暂存区的改动,且没有确认提示。真丢了先别慌,用 git reflog 找到 reset 之前的提交号,再 git reset --hard 提交号 救回来。reflog 默认保留 90 天。
2. 强推覆盖别人的提交
git push -f 会把远程历史换成你本地的,别人刚推的提交直接消失。非要用不可时加 --force-with-lease,它在远程有你不知道的新提交时会拒绝推送。
3. .gitignore 对已跟踪文件无效
文件已经被提交过,再加进 .gitignore 也不会生效。要先把它们从索引里移除:
git rm --cached config/local.yml
git commit -m "chore: 移除本地配置"
4. 冲突标记没删干净
合并冲突后文件里会留下 <<<<<<<、=======、>>>>>>>。解决冲突要手动改成最终内容,再 git add。提交前全局搜一遍这些符号,避免把标记带上线。
5. 把密码和密钥提交进仓库
配置文件里写了数据库密码,提交后再删也还在历史里。正确做法是提交前就用环境变量或配置中心,发现泄露后第一时间改密码,再考虑清理历史。
排查步骤
- 任何操作前先
git status,确认当前分支和改动范围; - 提交前
git diff和git diff --staged各看一遍,防止带上调试代码; - 操作失误立刻
git reflog,找到操作前的提交号; - 推送被拒绝时先
git pull --rebase,不要直接强推; - 合并冲突解决完,跑一遍编译和测试再提交。
评论