资讯动态

Git本地仓库超全命令详解:从入门到后悔药全掌握

发布时间:2026/10/17 11:13:09 来源:尧图企业网站定制
刚把远程仓库的一整套流程跑顺的朋友回头再看本地仓库的命令往往会有一种“原来这才是我天天要用的东西”的感觉。Git 系列写到第五篇我打算把本地仓库的常用命令彻底捋一遍。这些命令不像 push、pull 那样有“跟服务器打交道”的仪式感但它们才是日常开发里点击频率最高的部分初始化仓库、暂存文件、提交历史、切分支、对比差异、后悔了再撤回来。桩桩件件都发生在本机操作错了影响范围也就自己这一个仓库反而是练手和学习 Git 内部原理最好的切入点。这篇文章适合两类人一类是刚学会 clone、push、pull准备深入理解 Git 工作流的新手另一类是已经用了很久 Git但一直靠几板斧混日子的老手——比如只会 git add . 加 git commit -m从来没碰过 git stash、git rebase 或者 reflog 的。我尽量把每条命令为什么这么设计、底层发生了什么、什么场景下该用哪个参数都讲透顺便把我自己踩过的坑一并交代。1. 本地仓库的核心逻辑三个区域先搞明白1.1 工作区、暂存区、版本库到底是怎么协作的很多 Git 教程一上来就让你敲命令结果你敲了半年还不知道 git add 到底做了什么。要理解本地仓库的命令必须先建立三个区域的心智模型。Git 的本地仓库在工作目录里隐藏了一个 .git 目录这是版本库本体。围绕它划分出三个区域工作区Working Directory就是你当前能看到的、正在编辑的文件所在的目录。你新建、修改、删除文件动作都发生在这里。暂存区Staging Area / Index一个中间缓冲地带对应 .git/index 文件。你执行 git add 后文件的快照就被登记到这个索引里但还没正式写进版本历史。版本库Repository提交历史真正存放的地方对应 .git 里的 objects 目录和 refs 目录。你执行 git commit 后暂存区的内容才会生成一个永久性的提交对象。用一个生活化的类比工作区是厨房的操作台暂存区是备菜盘版本库是冰箱。你把菜文件从冰箱里拿出来放操作台上加工修改文件切好配好装进备菜盘git add觉得没问题了再放进冷冻层永久保存git commit。冷冻层里的东西不会轻易变质这就是为什么提交是安全的操作而工作区和暂存区的改动随时可能丢失。1.2 文件在 Git 眼里只有四种状态Git 根据文件与三个区域的关系把工作区里的每个文件分成四种状态Untracked未跟踪新文件还没被 git add 过Git 完全不认识它。Unmodified未修改文件内容和版本库里的记录完全一致。Modified已修改文件在工作区被改过但还没执行 git add。Staged已暂存文件改动已经登记到暂存区准备提交。理解这四种状态你就能看懂 git status 输出的每一行。很多初学者看到红色和绿色两栏就慌了其实红色对应工作区有改动但没 add 的文件绿色对应已经 add 进暂存区、等待提交的文件。颜色本身没有对错只是告诉你动作做到哪一步了。2. 初始化与配置git init 和 git config 的细节远比你想的多2.1 git init 之前必须想清楚的三件事创建一个本地仓库只需要一条命令git init执行后 Git 会告诉你 Initialized empty Git repository in .../.git/然后你就能在这个目录里正常使用所有本地命令了。但我建议你在敲这条命令之前先确认三件事。第一目录层级。Git 仓库的根目录就是执行 git init 时所在的目录它会递归覆盖所有子目录。如果你把仓库建在了错误的层级比如在用户主目录执行了 git init那整个家目录都会被纳入版本控制随手一个 git status 就能列出一大堆你根本不想管的文件。我见过有人把仓库建在根目录后果就是每次操作都慢如蜗牛因为 Git 要扫描整个硬盘的文件。所以执行前先 pwd 确认一下当前路径。第二是否真的需要在这个目录建仓库。有些目录是纯资源文件、临时脚本或者一次性测试代码建了仓库反而增加心智负担。仓库数量贵精不贵多一个项目一个仓库是底线。第三要不要用 --bare 参数。git init --bare 会创建一个没有工作区的纯版本库只存历史记录通常用在做团队共享的裸仓库服务器。日常开发里几乎用不到但如果你打算自己搭一个 Git 服务器迟早会碰到它。另外Git 允许你误操作后在已有仓库里再次执行 git init它会安全地重新初始化保留现有所有配置和提交历史不会清空任何东西。这算是一道保险但别因此就随意敲。2.2 git config 的三个作用域和它们的优先级仓库建好之后第一件事应该是配置身份信息。Git 每次提交都必须知道“谁在提交”这个信息记录在 user.name 和 user.email 两个配置项里。配置分三个作用域--system系统级对所有用户生效配置文件在 /etc/gitconfig日常个人开发基本用不上改它需要管理员权限。--global用户级对当前操作系统用户的所有仓库生效配置文件在 ~/.gitconfig这是个人开发最常用的一层。--local仓库级只对当前仓库生效配置文件在 .git/config。多账号工作的人比如一台电脑上既要提交公司项目又要提交个人项目就必须用这一层。三者的优先级是从小到大仓库级 用户级 系统级。也就是说如果三处都配置了 user.nameGit 最终会取仓库级的值。配置命令长这样git config --global user.name 你的名字 git config --global user.email 你的邮箱查看所有生效配置git config --list git config --list --show-origin # 顺带显示每条配置来自哪个文件检查某个具体配置项git config user.name我个人的习惯是全局配置设一个默认身份然后接手公司项目时立刻在仓库里用 --local 覆盖成公司身份。这样即使用户级配错了仓库级也能纠正过来不会出现提交历史里挂了错误身份名的尴尬。2.3 换个默认分支名省得以后麻烦新版 Git 初始化仓库时默认分支名已经从 master 改成了 main但如果你用的是旧版本或者团队习惯还没统一看到的分支可能就是 master。改默认分支名有两种做法一是在 git init 时直接指定git init -b main二是改全局配置git config --global init.defaultBranch main这个细节值得重视是因为分支名本身没有技术含义但团队协作时所有人统一用同一个默认分支名能省掉很多解释成本。早期 master 这个词在部分语境里有历史包袱新的 main 更中性也已经是当前生态的主流选择。3. 文件状态管理add、rm、mv 和 .gitignore 的完整用法3.1 git status 输出的每一行都别放过git status 是使用频率最高的命令之一但很多人只看一眼“有没有红字”就关了白白浪费了这个命令的信息量。执行后输出分成几块On branch xxx当前分支名。Your branch is up to date with origin/xxx本地与远程的同步状态纯本地仓库没有远程时不会显示这一条。Changes to be committed绿色区块已经暂存、等待提交的文件。Changes not staged for commit红色第一栏已在版本库里跟踪、但在工作区被修改、还没 add 的文件。Untracked files红色第二栏完全没被跟踪的新文件。还有个很有用的参数git status -s短格式输出每个文件前面两个字符表示暂存区和工作区的状态对比。比如 M 在第一列表示已暂存的修改在第二列表示未暂存的修改?? 表示未跟踪。这也是很多编辑器里文件树状态图标的来源。看惯了 -s 的输出比长格式高效得多。3.2 git add 的三种操作范围别再只会 git add .git add 把工作区的内容登记到暂存区但不同参数背后的“范围控制”完全不同。最常用的是按路径添加git add app.js git add src/ # 添加整个目录 git add --all # 等价于 git add -A所有改动和新文件 git add . # 当前目录及子目录的所有改动git add . 和 git add -A 的区别是初学者最容易忽视的坑。git add . 只影响当前目录及子目录而 git add -A 会处理整个工作区包括你在仓库其他目录里的改动。如果你在子目录里执行 git add .上层目录的改动不会被加进去。所以习惯在仓库根目录操作时两者等价但一旦你 cd 到子目录再执行 git add .就可能漏掉东西。还有两个进阶参数很值得练手。git add -u 只暂存已在跟踪的文件的修改和删除不会处理新文件适合你的改动里既有修改又不想把新建的临时文件拉进来的场景。git add -p 则是交互式地逐块暂存Git 会把文件的每个 diff 块逐个展示出来问你要不要暂存这一块。代码review到自己改动的每个细节用 -p 强迫自己思考每一处改动是否真的需要提交。3.3 删除和重命名的正确姿势删除文件时直接在工作区 rm 是效率最低的因为 Git 会把它识别成“已跟踪但被删除”你还得再 git add 一次。正确做法是git rm file.txt这个命令一步到位删除工作区的文件同时把删除动作登记到暂存区。如果你已经手动删了文件可以补一个 git rm --cached 来处理一种特殊情况想把文件从 Git 跟踪里移除但保留工作区的文件。这种场景常见于你准备把某个配置文件加进 .gitignore但不想删除它本身。执行git rm --cached config.json之后这个文件变为未跟踪状态再把名字加进 .gitignore后续改动就不会再出现在 git status 里。重命名文件也是同理直接 mv 之后 Git 看到的是一次删除加一次新增虽然仓库能用差异比对识别出是重命名但提交记录不够干净。用git mv old.js new.js一步完成工作区移动和暂存登记历史追踪也更清晰。3.4 .gitignore 的匹配规则与自查方法.gitignore 是每个仓库的标配但它的匹配规则让不少人吃过亏。几条核心规则空行和 # 开头的行为注释。匹配任意数量字符? 匹配单个字符[...] 匹配字符集合。/ 在末尾表示只匹配目录如 build/ 表示 build 目录及目录下所有内容。开头不带 / 的模式匹配任意层级的同名文件如 *.log 会匹配所有目录下的 .log 文件。开头带 / 的模式只匹配仓库根目录下的对应文件如 /todo.txt 不会忽略 src/todo.txt。! 开头的模式表示取反重新包含之前被忽略的文件但取反不能越过父目录的忽略规则。自查某条忽略规则是否生效用git check-ignore -v build/a.js这个命令会告诉你这条文件是被哪条规则匹配掉的规则内容写在哪个文件里。调试 .gitignore 时比瞎猜高效得多。再者.gitignore 只对未跟踪的文件生效。一个文件如果已经被 git add 并提交过你把它写进 .gitignore 不会产生任何效果它仍然会被 Git 跟踪。所以要把已跟踪的配置文件“赶出去”还得回到上面说的 git rm --cached。4. 提交与历史commit、log、show 的实用组合拳4.1 git commit 的提交信息与参数选择提交是整个 Git 工作流的枢纽暂存区积累的改动在这一步变成永久历史记录。最基础的提交git commit -m feat: 新增用户登录功能-m 直接附带提交信息不加的话 Git 会打开编辑器让你填写。很多人不喜欢编辑器界面但我觉得在编辑器里写提交信息有个好处它会显示将要提交的文件列表和本次改动的 diff 统计提交前能再确认一遍内容。git commit -a 是个偷懒参数效果等于对所有已跟踪的修改文件先执行 git add 再提交。注意它不会处理新文件所以 -a 加新文件是漏不进去的。建议别过度依赖 -a显式 add 能让你每次提交前都清楚自己在提交什么。还有一个高频参数 --amend用来修改上一次提交git commit --amend -m 新的提交信息它会把你暂存区的内容合并进最近一次提交而不是生成一个新提交。用这个命令可以修正提交信息、漏掉的文件、或者后悔的改动。但有个禁忌只在提交还没推到远程、或者没被其他人拉取时使用。因为 --amend 会重写提交对象改变提交的哈希值会导致别人的历史和你不一致。本地仓库没有这个烦恼但一旦涉及共享分支就要格外小心。4.2 git log 的花式输出找到你需要的那条历史git log 默认输出的样子对新手不太友好一大段一大段的哈希值、日期、作者信息堆在一起。看历史和提交信息我的建议是直接用下面几个组合git log --oneline git log --oneline --graph --all git log --oneline --decorate--oneline 每条提交压缩成一行前面是缩写的提交哈希后面是提交信息首行一屏能看几十条记录。--graph 用 ASCII 字符画出分支合并走向配合 --all 可以看到所有分支的提交拓扑这是理解 Git 分支结构最直观的方式。--decorate 会在分支名和标签名出现的位置做标注一眼看出 HEAD 指向哪里。按作者过滤git log --author某开发者按时间过滤git log --since2 weeks ago git log --until2025-01-01按内容过滤git log -S 某个函数名 # 找出添加或删除该字符串的提交 git log --grepfix # 提交信息里包含 fix 的提交-S 这个参数特别适合排查 bug——你知道某段代码是哪次改动引入的但忘了提交号直接搜字符串历史就能定位到那次提交。4.3 git show 不只是看某次提交git show 可以查看任意提交的详细信息git show HEAD # 最近一次提交的改动 git show abc1234 # 指定提交哈希 git show HEAD~2 # 最近第二次提交 git show HEAD:src/app.js # 查看某次提交时该文件的内容最后一个参数很实用你想看历史某个版本里某个文件长什么样不用 checkout 切换分支直接 git show 出来即可。这比来回切分支安全多了。git diff 和 git show 的关系要分清git diff 比较的是两个状态的差异git show 则是看某个提交本身包含的完整改动。前者用于 review 未提交的改动后者用于回看某次历史提交做了什么。5. 分支管理branch、checkout、switch 的现代用法5.1 git branch 的增删查改分支是 Git 最强大的设计本质只是一个指向某个提交对象的指针。创建分支git branch feature-login创建并切换一步到位git branch -b feature-login其实更常用的是 git checkout -b 或新版 git switch -c后面会细说。列出所有分支git branch git branch -v # 附带各分支最近一次提交信息 git branch --merged # 已合并到当前分支的分支删除分支git branch -d feature-login # 安全删除要求分支已合并 git branch -D feature-login # 强制删除不检查合并状态-d 和 -D 的区别我建议记牢-d 会先检查这个分支是否已合并进当前分支没合并就拒绝删除避免你丢掉一整条还没合入的工作线。如果你确定那个分支确实不要了才用 -D 强删。我刚用 Git 时经常因为 -d 报错就直接上 -D后来发现大部分报错其实是提示我该去 merge 而不是强删。重命名分支git branch -m old-name new-name改完名字注意未推送的分支随便改已经推送远程的同名分支本地改名后还要在远程做对应改动否则远程分支还是旧名字。5.2 checkout 和 switch 到底有什么区别git checkout 是个老牌命令职责太多既能切换分支又能恢复文件还能创建分支。功能强大但语义混乱这也是官方后来推出了语义更单一的 git switch 和 git restore 的原因。新命令的对应关系git checkout branch → git switch branchgit checkout -b new-branch → git switch -c new-branchgit checkout -- file → git restore filegit switch 只管分支切换git restore 只管文件恢复名字即是语义记忆成本大幅降低。我的建议是新用户直接学 switch 和 restore老用户慢慢迁过去。日常写代码敲 checkout 也不会出错但跟人交流时容易产生歧义——“我把文件 checkout 了”到底是切了分支还是还原了文件用 switch 和 restore 表达就再也不会混淆。5.3 分支切换前务必注意的脏工作区问题切换分支时Git 有一个底线规则你工作区的未提交改动不能和分支切换发生冲突。如果你想从 main 切到 feature而 main 上有未提交的修改恰好 feature 分支上这些文件的内容不同Git 会拒绝切换报错说你的本地改动会被覆盖。这其实是 Git 在保护你。强制切换有参数git switch -f feature但这是个危险动作未提交的改动会直接丢失。所以遇到切不过去的场景正确做法是先把改动处理好要么 git commit 提交掉要么 git stash 暂存起来。关于 stash 下面专门讲。干净切换的另一个建议是配合工作区状态检查养成习惯git status --short看到输出为空再切分支心里踏实得多。6. 差异比对git diff 的三种比较场景6.1 工作区、暂存区、版本库之间的两两对比本地仓库里差异比对发生在三个维度工作区和暂存区git diff这是你改动之后、add 之前最常用的命令查看工作区相对暂存区改了什么。暂存区和版本库git diff --cached当你已经 add 但还没 commit 时用这个命令看暂存区相对最近一次提交改了什么。经常有人只 add 不 commit晚上复盘想看自己攒了一天的活儿都有啥就是这条命令。工作区和版本库git diff HEAD直接对比工作区当前状态和最近提交的差异等于把前两个命令的结果合并展示。适合想一口气看完所有未提交改动时使用。每个参数之间差异比对的操作对象有细微区别但本质逻辑一致git diff 永远是拿两个状态做比较看“从 A 到 B 发生了什么变化”。搞明白这个模型任何 diff 参数都能推算出来。6.2 diff 输出怎么读到有价值的信息git diff 的输出对新手很不友好满屏的 和 -。其实只需留意几个部分diff --git a/app.js b/app.js被比较的文件。index 和 ---/旧文件和新文件的标识。 -12,5 12,8 变更位置-12,5 表示旧文件从第12行开始、连续5行12,8 表示新文件从第12行开始、连续8行。红行以 - 开头是被删掉或修改前的行绿行以 开头是新增或修改后的行。加一些参数让输出更易读git diff --stat # 只显示每个文件的改动行数统计 git diff --word-diff # 按词粒度显示差异适合大段文本修改 git diff --color-words # 带颜色地高亮改动单词 git diff -w # 忽略空白的差异demo 阶段有时候改动太多、不想一条条看就先用 --stat 看概览再针对单个文件用git diff app.js逐个文件看信息密度比一次性看所有文件高得多。7. 后悔药系列stash、reset、reflog 的实战用法7.1 git stash 把半成品安全地收起来写代码写着写着突然要切到另一个分支修个紧急 bug但手头的改动还不完整、不想提交怎么办git stash 是你的救星git stash push -m 用户列表页开发中它会把你工作区已跟踪文件的改动和暂存区的状态打包保存起来然后让工作区恢复到干净状态你可以放心切分支。恢复暂存的改动用git stash list git stash apply stash{0} # 恢复到工作区但保留 stash 记录 git stash pop # 恢复到工作区并从 stash 列表里移除apply 和 pop 的取舍pop 是“应用并删除”apply 是“应用但保留”。建议刚开始多用 pop保持 stash 列表干净如果拿不准要不要恢复先 apply确认没问题再手动 git stash drop。stash 的暂存内容默认只包含已跟踪文件的改动新文件不会被打包。想连未跟踪的新文件一起暂存加 -u连被 .gitignore 忽略的文件也要打包加 -a。但注意 -a 会把编译产物、临时文件一并收进去恢复时可能造成意外覆盖一般只用到 -u 就够了。7.2 git reset 的三种模式每个都是不同力度的后悔药git reset 用来把当前分支的 HEAD 指针往回移动。它有三个模式参数git reset --soft HEAD~1 # 只移动 HEAD暂存区和工作区都保留改动 git reset --hard HEAD~1 # 彻底回退工作区、暂存区都回到目标提交状态 git reset --mixed HEAD~1 # 默认模式移动 HEAD 并清空暂存区但工作区保留改动用场景来区分--soft你觉得上一条提交信息写得不对但改动内容都是对的。撤销提交但保留暂存区然后重新 commit 换一条信息。--mixed上一条提交混进了不想提交的文件。撤销提交把改动全部退回工作区重新挑拣后再 add 和 commit。--hard上一条提交整个就是错的改动也不要了彻底回到上一次提交时的状态。这三个模式的记忆方法soft 只动 HEADmixed 动 HEAD 和暂存区hard 动 HEAD、暂存区和工作区。力度从左到右递增。特别提醒 --hard 的不可逆性。一旦执行工作区那些未提交的改动直接蒸发。除非你运气好提前做了提交或者 stash否则没有挽回手段。所以任何 --hard 操作之前先问自己一句这些改动我真的不要了吗7.3 reflog 才是真正的终极后悔药git reset 有一个无法绕过的疑问如果我 reset --hard 之后才发现刚才的改动其实有用怎么办答案是 git reflog。reflog 记录了本地仓库所有 HEAD 移动的历史包括 reset、commit、checkout 等操作对 HEAD 的每次变更。你误删的、误回退的提交只要还躺在对象库里没有被垃圾回收reflog 里就能找到它的哈希。查看git reflog输出类似abc1234 HEAD{0}: reset: moving to HEAD~1 def5678 HEAD{1}: commit: feat: 用户中心功能如果你想恢复到那个 def5678 提交直接git reset --hard def5678状态瞬间还原。这也是为什么我建议任何误操作之后先别慌更别直接删 .git 目录reflog 大概率能捞回来。reflog 的保存时长默认是 90 天过期后相关提交可能被清理所以发现问题后趁早行动。8. 本地仓库高频问题与排查技巧实录8.1 提交之后发现漏了文件怎么补救提交完发现还有一个文件没加进去但不想制造一条无意义的“补提交”。处理方式git add forgotten.js git commit --amend --no-edit--no-edit 表示沿用上一条提交信息直接把漏掉的文件合并进上次提交。注意这只是重写了本地上一次提交如果这个提交已经推送过远程不建议这么做改成再发一条新提交更稳妥。8.2 想放弃当前改动但不同场景姿势不同“不要了”这个需求也要区分对象放弃工作区里某个文件的修改git restore app.js放弃暂存区里的内容让文件回到工作区git restore --staged app.js放弃新创建的未跟踪文件rm app.js # 或按需处理这个组合拳覆盖了大部分“撤销”场景。新命令 restore 的语义比老命令 checkout 清晰得多强烈建议直接用。8.3 分支名写错了但合并已经发生分支出生时名字起错了比如 feature-loging 拼错了正确做法是马上重命名git branch -m feature-loging feature-login如果这个分支已经合并过、不小心被删了别怕reflog 能查到删除前的提交哈希基于那个哈希重建分支即可git branch feature-login 原哈希这个技巧在本地仓库玩得转换到远程仓库就要注意重命名后同步的问题了。8.4 检查仓库状态和文件完整性最后顺带提一个排查用的命令git fsck它检查对象库的完整性常见场景是怀疑 .git 目录损坏或者文件系统异常。正常情况下没有输出有缺失对象时会列出问题编号。这个命令属于“基本用不到但用到就是救命”的类型。结尾一点个人经验整套本地仓库命令梳理下来我自己最大的体会是Git 的命令并不难背难的是搞清每条命令在“三个区域”模型里的位置。你只要时刻问自己“我现在操作的是工作区、暂存区还是版本库”大部分命令的语义都会自然浮现。另一个体会是别怕出错本地仓库的所有操作都是可逆的reflog 就是兜底的安全网。我早期因为不熟悉 reset --hard 丢掉过不少代码后来养成了“任何危险操作前先 stash 或提交”的习惯就再也没慌过。最后分享一个小技巧把下面这行别名加进你的全局配置日常查看状态和历史会顺手很多。git config --global alias.st status -s git config --global alias.lg log --oneline --graph --all --decorate以后敲 git st 和 git lg 就能快速看到仓库全貌。这套本地命令练熟了后面讲远程协作、合并冲突、多人协作流程时你会发现自己已经做好了准备。先把一个仓库管明白再谈团队协作这个顺序永远不会错。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑