☰
Git命令场景化实战:从版本控制原理到日常开发高手指南
2026/10/6 16:24:40 网站建设 项目流程

我一直觉得,Git 这东西有个特别尴尬的阶段:网上教程看了不少,add、commit、push这几个命令也会敲,可真到了项目里,遇到“改错了想回退”“分支搞得一团乱”“远程仓库连不上”这些场景,大脑还是一片空白,只能老老实实打开浏览器重新搜一遍。

这份笔记其实就是我自己的学习记录,目标只有一个:** 把 Git 命令从“背下来”变成“按场景查表”**。我整理的都是日常开发里最高频的操作,配合每个场景背后的原理和坑,这样下次手忙脚乱的时候,翻一眼就知道该敲哪条命令,以及为什么是这条命令。

如果你刚接触 Git,或者和我之前一样——“命令认识我,我不认识命令”,那这篇笔记应该能帮你省掉不少瞎折腾的时间。我会尽量用直白的说法把原理讲清楚,也会把我在真实项目里踩过的坑一并写出来,这些可都是文档里不会告诉你的东西。

1. 先搞清楚 Git 到底解决了什么问题:从“命名灾难”说起

在我们聊具体命令之前,我觉得有必要先花两分钟聊聊 Git 存在的意义。搞清楚这个,后面所有命令内存负担能减轻一半。

1.1 没有版本管理时的“命名地狱”

我最早写代码的时候,项目文件夹长这样:

毕业论文_final.doc 毕业论文_最终版.doc 毕业论文_最终版2.doc 毕业论文_再也不改了.doc 毕业论文_改完这版真的不改了.doc

是不是很熟悉?没有版本管理工具的时候,我们只能靠文件名来区分不同阶段。问题是,这种办法根本说不清楚“最终版2”和“最终版1”到底改了什么,俩人合作的时候更是一场灾难——你改你的,我改我的,最后合并根本不知道谁覆盖了谁。

Git 解决的就是这个问题:它像一个“时光机 + 无限容量的差异记录器”,把每一次修改都记录成一个带时间戳、带作者、带修改说明的快照。你随时可以回退到任何一个历史状态,也可以把不同人的修改合并到一起,还能知道每一行代码是谁在什么时候因为什么改的。

1.2 Git 的存储思维:快照,不是备份

这里有个关键认知:Git 不是简单地做“文件备份”。备份是整份复制——你存了十份,磁盘占用就是十分;而 Git 每次提交(commit)保存的是一个“快照”,它记录的是** 那一瞬间所有文件的完整状态**。只不过 Git 做了优化:文件没变的部分,它不会重复存储,只存一个指针指向上一个版本;变了的文件,它压缩后存起来。

所以 Git 仓库再大,.git目录通常也是可控的,这就是为什么你能放心地提交成百上千次,而不必担心磁盘爆掉。这个“快照”思维其实贯穿了 Git 的所有操作,尤其是后面要讲的checkout、reset,理解了快照,就理解了它们为什么是那个行为。

1.3 我的推荐学习路线:场景驱动

网上有海量的 Git 命令表,照着敲一遍很快忘光,因为命令脱离了场景。我后来换了个思路:** 只记“我什么时候会用到什么”**。

高频场景其实就那么几个:

  • 把改好的文件记录下来 →add+commit
  • 看看到底改了什么 →status+diff+log
  • 改错了想反悔 →restore/reset/revert
  • 多条线并行开发 →branch+merge/rebase
  • 和远程仓库同步 →push/pull/clone

下面几章,我会按这个顺序把这些命令逐一拆开讲,每条命令都会说清楚“什么时候用”“为什么这么用”“有什么坑”。

2. 环境配置那点事:装好 Git 以后最易被忽略的隐形坑

很多人装完 Git 就直接开干,结果第一行命令就报错,或者每次 push 都让你输密码,烦到不行。这个阶段其实有四个小步骤,做一遍能省下后面无数事。

2.1 安装:三句话说完

  • Windows:去官网下载安装包,一路 Next。唯一要注意的是安装时会有个“调整 PATH 环境变量”的选项,务必选“Git from the command line and also from 3rd-party software”,不然有些终端工具里找不到 git 命令。
  • macOS:装 Homebrew 的话直接brew install git;不想装 Homebrew,装 Xcode Command Line Tools 也行,它会自带 Git。
  • Linux(Debian/Ubuntu 系):sudo apt install git。CentOS/RHEL 系则是sudo yum install git。

装完验证一下:git --version,能输出版本号就说明装好了。这一步很简单,但确实是后面所有操作的地基。

提示:我见过不少新手装完 Git,命令提示符不刷新,直接去敲git报“不是内部或外部命令”。别急着重装,关掉终端再开一个,或者重启一下终端工具,多半就好了。

2.2 配置身份:不配置不许提交

Git 设计上有个强制要求:每次提交必须带着作者信息,否则它不让你提交。所以第一件事是配置用户名和邮箱:

git config --global user.name "你的名字" git config --global user.email "你的邮箱"

这里有个坑,我从同事身上见过太多次:GitHub、GitLab、Gitee 这类平台上的账号邮箱,和你本地配置的邮箱最好是同一个。不然你提交的记录在平台上是两个毫无关联的人,贡献图和代码统计全乱了。我个人建议直接查一下自己远程仓库里绑定的邮箱,配置成一样的。

想确认配置有没有生效,执行git config --list就能看到。还有一种情况:公司项目可能会要求你覆盖全局配置,那就在具体项目里不加--global,给该仓库单独配置:

git config user.name "公司内部名字" git config user.email "公司邮箱"

2.3 第一次 commit 编辑器:把 vim 基础命令补上

当你在终端里敲git commit(不带-m参数)时,Git 会打开一个文本编辑器让你填提交说明。系统默认的编辑器通常是 vim(Linux / macOS 上尤为常见)。

很多新手在这一步直接卡死:进去之后不知道怎么输入、不知道怎么保存退出,最后直接关掉终端,一脸懵。

你只需要记住这三组命令就能毕业:

  • 按i进入输入模式,这时候才能打字写提交说明
  • 写完后按Esc退出输入模式
  • 再输入:wq回车,表示保存并退出

w是 write(写入),q是 quit(退出)。如果中途不想改了,用:q!强制不保存退出。

但说实话,日常提交通常我都是直接git commit -m "提交说明",这样压根不用进 vim。揪着 vim 不放的场景是:你忘了写-m,或者需要写多行提交说明。不过既然入了 Git 的门,vim 这三板斧还是值当记一下的。

关于编辑器,也可以一开始就换成你熟悉的那个,比如 VS Code:git config --global core.editor "code --wait"。这样以后忘带-m,弹出的就是 Visual Studio Code,编辑体验会友好很多。

2.4 免密配置:别在密码验证上浪费时间

我最早做 pull / push 时,每次都要输账号密码,烦到怀疑人生。后来才知道可以把本机的 SSH 公钥配置到远程仓库平台上,之后所有操作都免密,清爽。

具体三步:

# 1. 生成密钥对(如果没有的话) ssh-keygen -t rsa -b 4096 -C "你的邮箱"

执行后它会问你把密钥存哪儿、要不要设密码,直接一路回车就行。这时候会在~/.ssh/下生成两个文件:id_rsa(私钥,自己留好)和id_rsa.pub(公钥,可以给别人)。

# 2. 查看公钥内容 cat ~/.ssh/id_rsa.pub

把输出的那一长串复制下来。然后打开你的 GitHub / GitLab 平台,找到SSH Keys或Deploy Keys的设置页面,粘贴保存。

# 3. 验证是否配置成功 ssh -T git@github.com

如果你用的是 GitHub,会看到类似Hi username! You've successfully authenticated的提示。之后克隆仓库时,记得选 SSH 地址而不是 HTTPS 地址,格式是git@github.com:用户名/仓库名.git。

这里有个高频坑:** 用 HTTPS 克隆的仓库,配了 SSH 公钥也不免密**。因为 HTTPS 走的是账号密码或 token 验证,跟 SSH 密钥是两套体系。解决方法:git remote set-url origin git@github.com:用户名/仓库名.git,把远程地址改成 SSH 形式,即可享受免密。

还有个相关的小问题:有些新人配置了 SSH 密钥,但还是报“Permission denied (publickey)”。多半是密钥文件路径没对上。Git 默认去~/.ssh/找id_rsa,如果你生成的时候改了文件名,或者把密钥放到了其他地方,Git 找不到,自然认证失败。解决办法是在~/.ssh/config里写清楚:

Host github.com HostName github.com User git IdentityFile ~/.ssh/你的密钥文件名

注意:私钥文件千万不要提交到 Git 仓库里,也不要发给任何人。它就是你远程账户的钥匙。泄露了钥匙,别人就能以你的名义改代码,后果不轻。

3. 提交记录的基本操作:三个区域搞明白,add / commit 不会再含混

刚开始用 Git 的人,最常困惑的就是git add和git commit到底什么区别,为什么不能一步到位。这背后其实是 Git 的“三个区域”设计,清楚了之后,很多命令不用背也推理得出来。

3.1 三个区域:工作区、暂存区、版本库

打个比方,你写文章:

  • 工作区(Working Directory):就是你的桌面,文件到处都是,改了一半的稿子也摊在上面。
  • 暂存区(Staging Area / Index):像一个“待提交清单”,你挑了几页干净的稿子,把它们放进一个信封,准备寄出去。
  • 版本库(Repository):就是收件箱。信封寄出去的那一瞬间,这些稿子才正式被收录、编号、归档。

对应到命令上:

  • git add 文件名→ 把工作区的修改放进暂存区(加入“待提交清单”)
  • git commit -m "说明"→ 把暂存区里的东西一次性归档到版本库,形成一个不可变的快照(commit)
  • git status→ 查看现在工作区和暂存区各处于什么状态

很多人问:为什么非要分两步?直接 commit 不行吗?这设计其实非常实用——你一个下午改了 5 个文件,很可能它们属于 3 个不同的逻辑改动。你可以只把“修复登录 bug”的两个文件add进来提交一次,再提交“调整页面样式”的另外三个文件,这样历史记录就条理清晰。这就是“暂存区”存在的核心意义:** 让你能够精细控制每次提交包含哪些内容**。

3.2 日常三连:status、add、commit

在一个项目里,我日常眼熟的命令流程是这样的:

# 1. 先看状态 git status # 2. 看具体改了啥 git diff # 3. 把需要的文件加进暂存区 git add index.html git add src/utils.js # 4. 提交 git commit -m "修复用户列表的加载卡顿问题"

git status输出里有两块信息,非常直观:Changes not staged for commit表示文件有改动但还没add;Changes to be committed表示已经add进暂存区、正等着commit。刚学 Git 的同学,看这个输出基本就能清楚自己操作到哪一步了。

如果改动的文件很多,想全部加入暂存区,可以git add .(当前目录及子目录所有改动),或者git add -u(仅跟踪已跟踪文件的所有改动,不包括新增文件)。我日常偷懒常用git add .,但拆提交时就会手动逐个add,保证每次 commit 的颗粒度合适。

git diff也值得拆一下:

  • git diff→ 看工作区改动,还没add的部分
  • git diff --cached→ 看暂存区跟上一个提交之间的差异,也就是你准备提交的内容
  • git diff HEAD→ 工作区 + 暂存区合起来,跟最近一次提交的差异

看 diff 的输出习惯之后,提交前扫一眼,能很大程度上避免“把调试用的console.log一起提交上去”这种尴尬。

3.3 git log:把历史记录读出花来

提交记录一旦多了,默认的git log输出会刷满屏幕。我常用的几个参数组合:

# 一行显示一条,清爽很多 git log --oneline # 带分支拓扑,看清各分支分叉合并 git log --oneline --graph --all # 查看指定文件的历史 git log --oneline -- 文件名

--oneline会把每个提交压成一行,左边是缩写 hash,右边是提交说明。配合--graph,分支的走向看得一清二楚,尤其适合处理复杂分支时用。

3.4 改完才发现提交说明写错了:commit --amend

这个命令我一开始完全不知道,后来在群里看到别人提了一句才去查。它解决的是这样一个场景:你刚git commit -m "修复bug",马上发现说明写得太笼统,或者文件里忘了一个空格,再或者漏提交了一个小文件——但你不想为此再产生一条“补丁式”的提交记录。

# 修改最近一次提交的说明 git commit --amend -m "修复用户列表加载卡顿,并补充边界处理" # 或者:把忘提交的文件加进去再合并到上一次提交 git add 忘提交的文件 git commit --amend --no-edit

--amend实际效果是:** 把当前暂存区的内容跟最近一次提交合并,生成一个全新的提交,替换掉旧的**。最后 Git 历史里看不到原来那条,只看到新的一条。

前提是这“最近一次提交”还没推送到远程。如果你已经git push了,再--amend会改变提交 hash,远程和本地历史就“分叉”了,要推上去必须用git push --force强制覆盖,这在多人合作的分支上非常危险——会把别人基于旧提交拉的分支统统弄乱。所以老规矩:** 本地刚写完觉得不爽就 amend,推上远程了就老老实实开新一轮提交**。

3.5 撤销的艺术:restore、reset、revert 怎么选

这可能是 Git 命令里最让人头大的一片,因为它牵涉到“撤销到什么位置”。我把它们拆成三个场景来讲:

场景一:工作区改乱了,想回到没改之前

git restore 文件名

这个命令会把工作区文件恢复到暂存区/最近提交的状态。注意:未add的改动会被直接丢光,恢复不了,操作前确认一下。

场景二:已经 add 进暂存区,想移出来(但保留改动)

git restore --staged 文件名

这个非常常用,我经常一不小心git add .把所有文件都加进去了,但实际上只想提交其中一半。用这条命令把不该提交的文件从暂存区“挪”出来,改动还在工作区乖乖躺着。

场景三:已经 commit ,想回退历史

这里要看你是想“彻底删除”还是“保留记录地反悔”:

  • git reset --soft HEAD~1:软回退。只移动 HEAD 指向,工作区和暂存区的内容全都保留。相当于撤销了一条提交记录但所有改动还在,重新整理后再次提交。
  • git reset --mixed HEAD~1:默认模式。回退到上一个提交,并且暂存区被清空,但工作区改动还在。适合你发现自己提交错了一堆东西想重新整理的场景。
  • git reset --hard HEAD~1:硬回退。彻底回到上一个提交的状态,工作区所有未提交的改动全部被删除。这条命令每执行一步都是“不可逆的”,我建议只在确定这些改动都不需要的时候才用。

三者的区别,我用这个表格记:

命令工作区暂存区版本库(提交历史)
--soft保留保留回退
--mixed(默认)保留清空回退
--hard清空清空回退

而当你已经把提交推送到远程、其他人可能已经拉取时,不管reset还是amend都会引发同步问题。这时候该用的是git revert <commit-hash>。它是** 创建一条“反向提交”**,把你之前那次提交做的改动撤销掉,生成一条新记录,历史不会被改写。多人协作时这是最安全、最体面的撤销方式,代价是历史上会多出两条记录(原来的 + 反向的),看着不够“干净”,但安全第一。

4. 分支操作的正确姿势:合并一次你就全懂了

分支是 Git 最核心也最强大的设计,没有之一。很多新手怕分支,总觉得是高级玩法,但其实只要理解了“分支只是指针”这个本质,就没什么神秘的了。

4.1 分支到底是个什么东西

你可以把分支理解成一根“可移动的便签”,贴在某次 commit 上。每提交一次,当前分支指针就跟着往前挪一格。

比如你从main分支上切出一个新分支dev,它俩一开始指向同一个 commit。之后你在dev上提交了两次,dev往前走,main还停在原地。这就是“两条开发线互不干扰”的本质——** 两个指针指向的位置不同而已**。

4.2 创建、切换、删除一气呵成

# 查看所有分支(带 * 的是当前分支) git branch # 创建新分支并切换过去(最常用) git checkout -b feature/login # 只创建不切换 git branch feature/login # 切换分支 git checkout feature/login # 新版 Git 推荐用下面这个,语义更明确 git switch feature/login # 删除分支 git branch -d feature/login

这里有个必须记住的坑:** 切换分支前,先保证工作区是干净的(要么提交了,要么 stash 了)**。否则你在分支 A 没提交的改动会因为 checkout 一起“漂移”到分支 B 去,轻则两个分支间内容串味,重则直接因为冲突拒绝切换。

如果确实有改到一半不想提交的文件,又需要立刻切分支处理问题,可以:

git stash # 把所有未提交改动存到一个临时区域 git checkout 其他分支 # 处理完了切回来 git stash pop # 恢复之前存起来的改动

stash这个命令我一度觉得冷门,但真正忙起来的时候救过我好几次命。忘了就查git stash list,想清空可以git stash clear,但小心——那是真没了。

4.3 合并:fast-forward 和三方合并

把一条分支的修改并入另一条,用merge:

# 先切到你“想要收编别人改动”的分支 git switch main # 把 dev 分支的改动合并进来 git merge dev

合并的时候,Git 会分两种情形:

第一种:fast-forward(快进合并)。当 main 自 dev 分叉出去后没有任何新提交,也就是 dev 领先于 main 一条直线,Git 只需要把 main 指针直接移到 dev 的位置即可,历史还是一根直线,干净利落。

第二种:三方合并(merge commit)。当 main 也有自己的新提交,两条线在分叉后各自往前走,Git 就必须找到“共同的祖先提交”和双方的最新状态,尝试融合出一个新提交。

这个场景下,一旦同一行的代码两边都改了,Git 就没办法自动决定,于是报冲突(conflict)。很多新手遇到冲突就慌,其实处理方法很固定。

4.4 冲突不可怕:完整走一遍

假设main分支和feature/login分支都改了README.md的同一行,merge 时你会看到:

CONFLICT (content): Merge conflict in README.md Automatic merge failed; fix conflicts and then commit the result.

打开README.md,里面会有这样的标记:

<<<<<<< HEAD 这是 main 分支上的内容 ======= 这是 feature/login 分支上的内容 >>>>>>> feature/login

<<<<<<< HEAD到=======之间是当前分支的内容,=======到>>>>>>>之间是正要合并进来的分支内容。你手工编辑这个文件,决定最终保留哪一段、删除哪些标记,然后:

git add README.md git commit -m "解决 README 冲突,合并两个分支的改动"

合并冲突就用这三步:** 手工改文件 → add → commit**。没有魔法,也不需要高级命令。唯一要留神的是,解决冲突时别把别人的改动整个覆盖掉了,逐行确认后再提交。多经历几次,你甚至会习惯这种“可控的人工裁决”。

如果你问merge和rebase怎么选,我的态度是:** 团队协作的分支上优先 merge,保证历史真实可追溯;个人开发或清理本地提交时可以用 rebase 让历史更线性**。Rebase 是“把一条分支上的提交拾起来、重新在另一条分支的顶端按顺序重放一遍”,看着历史是直线,但相当于改写了提交记录,所以公共分支上别乱用。

4.5 删除分支的 -d 与 -D

git branch -d 分支名只允许删除“已经被合并过”的分支,这是安全保护。如果你想删的是一个还没合并、改动只有自己知道的本地分支,Git 会拒绝并提示:

error: The branch 'xxx' is not fully merged.

这时候需要用-D强制删除:

git branch -D 分支名

我自己的体会是:-d拒绝时先别急着-D,确认一下这个分支真的没用了。万一是该 merge 到你所在分支而你忘了 merge,那-D会把整条改动丢进回收站找不回来。宁可先切过去看一眼git log,再决定动不动手。

5. 远程仓配的经典连环坑:从 clone、push 到 SSH 认证失败

本地玩得再花哨,最终还是要跟远程仓库打交道。这一段的命令不多,但坑是真的多。我按一个项目的诞生顺序来讲。

5.1 从零连接一个远程仓库:git remote

假如你在 GitHub / GitLab / Gitee 上新建好了一个空仓库,本地也已经有项目了,连接的方式:

# 把远程仓库地址命名为 origin(约定俗成的默认名) git remote add origin git@github.com:用户名/仓库名.git # 查看连接情况 git remote -v # 如果发现地址填错了,想改 git remote set-url origin 新地址 # 如果想删掉远程关联 git remote remove origin

这里最容易被忽略的:git remote add origin只是建立了一个“远程仓库的快捷方式”,它本身不会推送任何代码。刚建完仓库的新手容易误以为已经“上传”了,一看远端还是空的就犯嘀咕。真正的上传靠的是下面这条:

# -u 表示首次建立本地 main 分支与远程 origin/main 的跟踪关系 git push -u origin main

第一次推送时加-u,以后在这个分支上只需git push,Git 就知道推送给谁了。

5.2 把远程仓库拉到本地:clone、pull、fetch

最常见的开始方式是克隆已有仓库:

git clone 仓库地址 git clone 仓库地址 自定义目录名

克隆下来的项目带有完整的 Git 历史。之后日常同步:

git pull

很多人对pull和fetch的区别也懵。其实git pull=git fetch+git merge。fetch只是把远端最新提交下载到本地(存在origin/main这条远程跟踪分支上),** 不动你工作区的代码**;而pull会自动接着执行合并,把你的本地分支跟远端同步。建议初期就记pull好了,等你需要精细化控制同步过程时,再拆分用fetch。

有冲突的时候,pull也会直接报冲突,解决方式和 merge 冲突完全相同——手工改、add、commit。

5.3 SSH 认证失败:一次完整的排查链路

“SSH 认证失败”“Permission denied (publickey)”是 GitHub 新手区的经典疑难杂症。我把自己排查这类问题的经验写成固定套路,供你照着走:

第一步:确认协议。先看你的克隆地址:

git remote -v

如果输出是https://github.com/...,那认证走的是 HTTPS 体系,SSH 密钥根本不会参与,你折腾半天公钥都没用。要么改用 SSH 地址,要么配 HTTPS 的 token。

第二步:确认密钥存在且路径正确。

ls -la ~/.ssh/

看看没有id_rsa和id_rsa.pub,不确定就先重新生成一份,然后确认是否把.pub的内容粘到了平台的 SSH Keys 设置里。

第三步:测试连接。

ssh -T git@github.com

你能看到Hi xxx!就说明认证通了;看到 permission denied 就继续往下走。

第四步:检查 ssh-agent 和 config。有些场景下密钥不在默认路径,或系统没自动加载密钥,手动指定一把试试:

ssh-add ~/.ssh/你的密钥文件名 ssh -T git@github.com

第五步:看具体报错信息里的关键词。比如port 22: Connection refused这类,就说明确实连不上服务器,排查方向直接从“认证”转向“网络连通性”了。

我建议每次排查都从git remote -v看协议开始,别一上来就重装 Git,八成不是 Git 的问题。

5.4 Git LFS:大文件别往普通仓库里塞

Git 的设计偏向保存文本源代码。你往仓库里放一个几百 MB 的设计稿、数据集、二进制安装包,仓库会越拉越慢,.git目录飞速膨胀。Git LFS(Large File Storage)就是用来处理大文件的替代方案——它把大文件的实际内容存到远端专用的存储服务里,Git 仓库里只放一个轻量的指针文本。

# 1. 装 LFS git lfs install # 2. 标记要追踪的大文件后缀/路径 git lfs track "*.psd" git lfs track "*.zip" git lfs track "assets/" # 3. 提交 .gitattributes(LFS 会生成或让你更新它) git add .gitattributes git commit -m "配置 LFS 追踪大文件" # 之后正常 add / commit / push 即可

我踩过的坑是:** 配置 LFS 之前,误把大文件直接git add进普通仓库过**,结果历史记录已经被大文件的完整内容污染,哪怕后来删了文件、提交新的版本,.git目录里的旧记录依然占着空间。要清理这种事后的“历史遗留大文件”,得用git filter-branch或git filter-repo重写历史,又复杂又有风险。所以最佳策略永远是:** 项目一开始就决定好哪些资源要走 LFS**。

还有个高频问题:同事问我“git lfs clone 为什么卡住”。其实git clone遇到 LFS 仓库时,Git 会并发从 LFS 存储下载大文件,网络慢或者平台限流,看起来就是一直卡在某个百分比。可以试试:

GIT_LFS_SKIP_SMUDGE=1 git clone 仓库地址

先跳过 LFS 文件下载拿到源码,之后按需用git lfs pull下载大文件。这样至少能先把代码看起来,不至于整个克隆进程卡死。

5.5 仓库里的敏感信息:这是底线问题

热词里有“git 目录泄露如何下载”,这个背后的本质其实是个很严肃的安全问题——** 如果项目根目录的.git文件夹被发布到了公开可见的位置,等于把整个仓库历史、所有分支的源码甚至历史密钥都泄露出去了**。对开发者来说,反过来的教训更值得记:永远不要把这个目录暴露给不该看到的人,也永远不要把.env、密钥文件、数据库密码这类敏感内容提交进去。

Git 提供了一套防线,最基础的是.gitignore和提交前的自查:

  • 敏感配置文件一律加进.gitignore
  • 提交前用git status和git diff扫一眼,看有没有意外带进去的文件
  • 一旦发现敏感信息已经被推到远程,不要再指望“删掉再提交一次”能抵消痕迹——历史记录里全在。需要立刻更换密钥、凭证,并把旧的信息作废
  • 真需要擦除历史的情况下,用git filter-repo这类工具重写历史后强制推送

我不建议展开讲任何“如何从泄露目录里捞东西”的操作,这里只有一句忠告:** 管好自己的提交物,敏感信息绝不入库,密钥泄露即刻作废重换**。这是每个拿 Git 做开发的人都该有的基本素养。

6. 踩坑笔记:过滤文件不生效,以及那些“疑难杂症”

最后一章,我来盘点几个我身边反复出现的“Git 疑难杂症”,基本都属于“上网搜了半天才发现是某个小配置问题”的类型。

6.1 .gitignore 写了却不生效:都是缓存惹的祸

太经典了。你把.gitignore配好,加上node_modules/、.env,但执行git status时这些文件还在列表里冒头,或者还是被git add .加了进去。

核心原因:**.gitignore只对“还没被 Git 跟踪”的文件生效**。如果某个文件在此之前已经被git add或git commit过,Git 已经把它的元信息记在索引里了,之后再写.gitignore拦不住它。

解决办法是把它从 Git 的索引里移除,但保留本地文件:

git rm -r --cached . git add . git commit -m "应用 .gitignore 规则,清理已跟踪文件的缓存"

第一条命令会把所有文件从暂存索引移除(工作区不动),再add .时 Git 会重新读取.gitignore,不该跟踪的从此就安分了。

顺带一提.gitignore写法的几个常见误区:node_modules匹配的是任何层级下的同名目录,/node_modules只匹配根目录下的;以/结尾表示只匹配目录;*通配符不匹配路径分隔符,**才可以跨目录。这些细节在排错时非常管用。

6.2 大小写改名的文件,Git 死活不认

你在 Windows 或者 macOS 默认文件系统上干活,把readme.md重命名成README.md,然后git status发现 Git 无动于衷,提交记录里看不出来。

这是文件系统“大小写不敏感”的锅。Git 默认配置core.ignorecase的值是 true,它会把单纯的大小写变化当成“没变化”。解决办法是显式告诉 Git 这次变化:

git mv README.md readme.md git mv readme.md README.md git status

或者更简单粗暴:临时把配置改掉,让 Git 开始敏感比较大小写:

git config core.ignorecase false

但注意,这个改动会影响当前仓库的大小写敏感性,改完之后对存量文件的处理要格外小心,建议只在确实需要重命名大小写的场景下临时开启。

6.3 换行符(CRLF/LF)引发的“整个文件都变了”惨案

Windows 上写的代码,一行结束用的是CRLF(回车+换行),Linux/macOS 是LF(换行)。如果你在 Windows 上改了某个文件的一个小地方,提交到 GitHub,同事在 Linux 上打开 diff 一看,整份文件全红了——原因是 Git 把换行符也当作改动一起比对了。

解决方案是配置 Git 的换行符自动转换:

# Windows 上: git config --global core.autocrlf true # Linux / macOS 上: git config --global core.autocrlf input

原理不展开讲,记住这个对照配置即可。true意味着:检出代码时把 LF 转成 CRLF(适配 Windows 编辑器),提交时把 CRLF 转回 LF(保证仓库里存的都是 LF)。input意味着:不转换检出的,只保证提交时统一为 LF。

如果你的团队已经有存量代码饱受换行符之苦,更靠谱的做法是给仓库加一个.gitattributes文件,按文件类型显式声明换行规则:

* text=auto *.js text eol=lf *.sh text eol=lf *.bat text eol=crlf

.gitattributes的优先级高于个人配置,能强制全团队保持一致,强烈建议项目初始就加上。

6.4 我关于“学习 Git 不靠硬背”的最后一点建议

这几章写下来,我最大的体会是:Git 命令本身不难,难的是把命令和场景对应起来。我自己的做法是维护一张“场景卡”,遇到新问题就往里加两条,现在已经积累了厚厚一沓:

  • “想回到上一个提交的状态但不想丢改动” →git reset --soft HEAD~1
  • “提交到本地但发现不该提交” →git reset --mixed HEAD~1
  • “改到一半想切分支” →git stash+git stash pop
  • “想撤销已推送的某次提交” →git revert <hash>
  • “想合并多条连续提交记录” →git rebase -i HEAD~n
  • “误删分支想找回” →git reflog配合git branch 分支名 <hash>
  • “只想暂时保存某个文件到远程但不进本地仓库” → 这种场景就去查git archive或者干脆别用 Git 干这事

git reflog值得单独提一嘴:它是 Git 的“操作日志”,记录了你每一次 HEAD 移动的历史。即使git reset --hard搞丢了提交,只要没到垃圾回收的时限,都能通过git reflog找回那个 hash。学 Git 的新人知道这个,能少哭几回。

至于“命令行提交代码”这件事,说实话一开始也别过度追求命令行全家桶,配上 VS Code 的 Git GUI、或专门的可视化工具,日常操作确实舒服很多。但我始终坚持:** 命令行是理解 Git 底层逻辑最好的方式**,图形化工具点几下完成的事,你可能完全不知道背后发生了什么,等出问题时无从下手。先把命令行这套过一遍,再回去用图形工具,心里会踏实得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询