Git实战手册:从核心原理到常用命令,一篇文章搞定版本控制
发布时间:2026/9/29 9:35:11 作者:尧图编辑部 阅读量:1,286

Git 这东西刚接触的时候真想摔键盘。明明我做的就是改两行代码怎么命令一敲报错跑出来一堆英文什么 detached HEAD、nothing to commit、fatal: not a git repository看着每个单词都认识合在一起就是不知道什么意思。我已经记不清用它跑了多少年的项目从最早把项目文件复制粘贴加日期命名到后来每天跟 Git 相爱相杀再到现在基本闭着眼睛敲命令这中间踩的坑、查的资料、总结的经验值得整理成一份真正能帮到人的东西。这篇不想写成官方文档就按实际使用场景来让从没碰过 Git 的新人快速入门让会一点但老报错的人查漏补缺也让用过但总忘命令的人随手翻。放心我不会让你背命令而是带你理解它为什么这么设计理解了之后很多命令你自己都能猜出来。1. 先搞清楚Git 到底解决什么问题很多教程上来就教你敲命令敲了半天也不知道自己在干嘛。我反而不着急先花点时间把 Git 的底层逻辑讲明白后面所有命令都是水到渠成的事。1.1 用一个比喻理解 Git你的代码时光机你玩过 RPG 游戏吗Git 就是给代码做的存档系统。你在打 BOSS 之前存个档打输了直接读档重来你在写代码的每个关键节点提交一次改崩了就能回到上一个存档点。关键区别在于这个存档不光你自己能读团队成员也能读还可以互相合并各自的存档进度。传统的做法是项目文件夹复制一份改成项目_20240101_最终版再改两天又来一个项目_20240103_最终版2最后整个磁盘里全是这种文件夹分不清哪个是最新的、哪段代码是谁改的、为什么这么改。Git 用一套统一的机制把这些问题全解决了每次存档叫一次提交commit存档之间按时间串成一条线叫提交历史history每条存档还能写一句说明告诉别人这次改了什么。这比任何文件夹命名都可靠。我见过很多新人第一次git commit之后特别兴奋说终于明白为什么大家都说“代码有救了”。其实原理就是这么简单——给代码存一个可回退的档。1.2 Git 和 SVN 的本质区别分布式 vs 集中式如果你接触过老项目大概率听过 SVN。Git 和 SVN 最核心的区别用一句话说SVN 是集中式Git 是分布式。SVN 时代所有代码都放在一个中央服务器上你想提交代码必须先把别人的最新代码更新下来解决完冲突才能提交因为大家共用一条“主路”。这个过程特别像办公室里的共享文件夹一次只能一个人改同一个文件效率低不说中央服务器一挂所有人直接没法干活。Git 不一样。每个开发者git clone下来之后本地就有一份完整的仓库包含所有历史、所有分支。你可以完全离线干活先在自己的本地仓库里提交一百次都没人管你等网络通了再推上去。这就像每个人手里都有一份完整的档案副本各自在自己的副本上工作最后按规则合并。分支在 Git 里也特别轻量创建一个分支就是一瞬间的事所以大家习惯性地为每个功能开一个分支改完再合回主干互不干扰。这个差异带来的实际感受是用 SVN 的时候我不敢随便开分支因为合并太痛苦用 Git 之后我一天开好几个分支反正不怕折腾坏了。1.3 先把这 4 个核心名词刻进脑子里后续所有命令都会反复用到这几个词先记住它们后面的路会顺畅十倍。名词一句话理解生活类比工作区你当前正在编辑的文件所在的目录你的工作台暂存区用git add把改动放进去的区域购物车本地仓库用git commit把改动正式保存的地方已经入库的档案库远程仓库托管在服务器上的仓库公共档案室工作台工作区上的东西不是放进购物车暂存区就会自动保存的也不是提交一次就自动放到远程的。这四个区域之间怎么流转就是 Git 命令在做的事。很多报错比如nothing to commit本质是你忘了把改动放进购物车fatal: not a git repository本质是你根本不在一个档案库的范围内。理解了区域流转报错信息就不再是天书。2. 从零到一安装、配置、连上远程仓库下载安装这类事看起来简单但往往是新手第一个翻车点。我把不同系统下的安装方式和装完后的必经步骤都列出来。2.1 Windows 和 Mac 上安装 Git 的几种方式Windows 上最省事的方式是去 Git 官网下载安装包一步一步点下一步就行。如果官网下载慢各大软件商店、主流技术社区的软件源里也都有 Git 的安装包镜像找一个可信的渠道下载即可这里不展开。除了图形安装包外如果你习惯用包管理器也可以一行命令搞定# Windows 使用 wingetWin10/11 自带 winget install --id Git.Git -e # macOS 使用 Homebrew brew install git # Ubuntu/Debian 系 sudo apt install git安装过程中有几个选项值得注意。第一个是默认编辑器很多人直接选了默认的 Vim后面第一次写提交信息就会被困在 Vim 里出不来第二个是 PATH 环境变量安装时默认选项通常没问题但如果你选了某个特殊选项导致终端里敲git提示“无法识别”大概率就是 PATH 没配好第三个是行结束符转换新手不用纠结保持默认即可后面真遇到换行符警告再处理。装完验证是否成功在终端里敲git --version如果弹出类似git version 2.40.0.windows.1的提示说明装好了。注意一定新开一个终端窗口再试否则环境变量不生效你可能会误以为自己装失败了。2.2 装完必做设置用户名、邮箱和默认编辑器Git 每次提交都会记录作者信息这个信息从哪来就是从你的全局配置里读。不配置的话提交时要么报错要么在一些提交记录里出现一串奇怪的默认名字。git config --global user.name 你的名字 git config --global user.email 你的邮箱这里的--global表示对当前用户的所有仓库生效。如果某个公司项目需要单独的身份可以去掉--global在对应仓库目录里单独设置优先使用仓库内部的配置。顺手把默认编辑器也配一下。如果你用 VS Code强烈建议执行下面这行git config --global core.editor code --wait这样以后一些特殊场景比如提交信息没写、或者后续要改写提交信息会自动打开 VS Code 让你编辑而不是弹出一个让你手足无措的 Vim。Vim 不是不能用但新人往往进去就不知道该按哪个键退出最后只能关终端然后整个生命周期都卡在一个锁文件上。我之前就见过同事因为这个把整个终端都关了一脸崩溃。配好编辑器之后这类坑直接消失。检查所有配置是否生效git config --list2.3 配置 SSH 密钥告别每次输密码用 HTTPS 方式拉代码每次 push 都要输入账号和密码时间一长真的很烦。更推荐的做法是配一套 SSH 密钥配完之后一劳永逸push、pull 都不需要再输密码。第一步在终端里执行ssh-keygen -t ed25519 -C 你的邮箱一路回车它会默认把密钥生成在~/.ssh/id_ed25519私钥和~/.ssh/id_ed25519.pub公钥。注意私钥绝对不能给别人就像家里的钥匙不能随便复制给别人一样公钥是拿来公开的可以放心配置到代码托管平台上。第二步查看公钥内容cat ~/.ssh/id_ed25519.pub把输出的那一长串复制下来打开 GitHub 或 Gitee 的设置页找到“SSH Keys”相关入口粘贴保存。第三步验证是否配置成功ssh -T gitgithub.com如果看到类似Hi xxx! Youve successfully authenticated的提示说明已经通了。之后在使用 Git 的时候远程地址选SSH格式的地址形如gitgithub.com:用户名/仓库名.git而不是 HTTPS 格式就能实现免密操作。2.4 SSH 认证失败的常见原因与排查我在各种群里看到最多的报错就是Permission denied (publickey)SSH 认证失败。排查思路其实很固定。首先验证密钥本身通不通。执行ssh -T gitgithub.com。如果提示成功认证说明密钥没问题问题多半出在远程地址上检查一下当前仓库的远程地址到底是不是 SSH 格式git remote -v如果显示的是https://github.com/...那就会一直要你输密码。改为 SSH 地址即可git remote set-url origin gitgithub.com:用户名/仓库名.git如果ssh -T这一步就失败常见原因有几个公钥没粘贴到平台上换了电脑但只复制了公钥、忘了把私钥放到~/.ssh目录或者私钥文件权限不对Linux/Mac 下要求私钥权限不能过于宽松。顺着这几个点查基本都能解决。还有一个容易被忽略的地方如果你配置密钥时输入了短语口令passphrase每次使用 SSH 时都会要求输入一次口令本质上没有达到“免密”效果。解决方案是在生成密钥时不要输入口令或者后续用工具把口令缓存住。新手图省事直接生成时全部留空回车最稳妥。3. Git 速成路线图按场景记命令比背命令快十倍不要按语法书去背命令要按场景去记。遇到什么需求用哪条命令配合什么参数这才是实际工作中真正需要的东西。我把日常工作里最常用的场景拆成了四组。3.1 本地提交init / add / commit / status / log你在本地新建了一个项目文件夹想让 Git 开始管理它。在文件夹内打开终端执行git init这一步会在当前目录生成一个.git隐藏文件夹整个项目就变成 Git 仓库了。然后创建文件、写代码做完一部分改动后先看下当前状态git statusgit status会告诉你哪些文件被修改了、哪些文件还没被 Git 跟踪、当前在哪个分支上。我每天上班第一件事就是敲一下这个命令心里立刻清楚了。改动确认没问题把文件放进暂存区购物车git add ..表示把当前目录所有改动都加进去。如果你只想提交某个文件就写具体文件名比如git add src/utils.js。放进暂存区后正式提交存档git commit -m 完成了登录功能的开发-m后面跟提交说明。提交说明的格式没有严格标准但强烈建议写清楚“做了什么”而不是“改了东西”。看完提交历史就能知道每一次改动的意图是团队协作的基本素养。最后看一眼提交历史git log --oneline --graph--oneline让每次提交只显示一行摘要--graph用线条把分支关系画出来。这个命令我一天会看十几次整个项目的脉络都藏在里面。3.2 分支操作branch / checkout / switch / merge分支是 Git 最强大的特性理解它之后你的开发习惯会直接升级。通俗理解分支就是平行宇宙。main或master是稳定的主线你要开发新功能时可以另起一个分支在这个分支上随便折腾不会污染主线的稳定性。创建分支git branch feature-login切换到该分支git checkout feature-loginGit 2.23 之后提供了更直观的新命令git switch不过checkout在旧教程里极其常见两种记一种就行。你也可以一步到位地创建并切换git switch -c feature-login在feature-login分支上开发完切回主线把分支合并进来git checkout main git merge feature-login合并完成分支已经没有保留的必要了删掉git branch -d feature-login这套「开分支 → 开发 → 合并 → 删分支」的流程就是大部分人日常使用 Git 的主旋律。养成习惯永远不要在main上直接改代码哪怕是一个人开发也尽量走分支这样随时可以从一个干净的main发版。3.3 后悔药restore / reset / revert / commit --amend写代码最不缺的就是后悔场景。Git 给你准备了几种“后悔药”但每种药的作用范围不一样用错可能更麻烦。场景一提交信息写错了或者提交时漏了某个文件。用--amend补救git add 漏掉的文件 git commit --amend这条命令会把你暂存区的新改动合并到最近一次提交里同时打开编辑器让你修改提交信息。注意--amend会改变提交的哈希值所以如果这次提交已经 push 到了远程、并且其他人可能已经基于它做了操作就不要随便 amend 了不然会造成提交历史错乱。还没推送前放心大胆用。场景二工作区文件改乱了想丢掉改动。用git restoregit restore 文件名它会把文件恢复到最近一次提交的状态。注意这个操作会直接丢弃你的未提交改动无法找回执行前一定要确认。场景三文件已经git add进暂存区想放回工作区。也是git restore加一个参数git restore --staged 文件名场景四提交完之后发现整个提交都有问题想回退。这里分两种心态。如果是本地仓库、还没推送的提交可以用git reset# 软回退撤销这次提交但保留改动内容在暂存区 git reset --soft HEAD~1 # 硬回退彻底回到上一个提交后面的改动全部消失 git reset --hard HEAD~1--hard是极度危险的操作它会把工作区、暂存区统统重置到指定提交之后的所有改动都不见了。我每次用--hard前都会先git log确认当前的提交哈希或者干脆只用--soft。如果是已经推送到远程的提交千万不要用reset因为远程仓库的提交已经在队友本地存在了你把历史改写了队友一 push 就冲突。正确做法是git revertgit revert HEAD它会生成一个“反向提交”把原来的改动撤销掉但完整保留历史记录这样所有人才会同步到一个正确的状态。回退操作里本地用reset远程用revert这是最重要的区分。3.4 远程协作clone / fetch / pull / push一个人的时候本地操作就够了。真正的战场是在多人协作里。拿到一个新项目先克隆到本地git clone gitgithub.com:用户名/仓库名.git提交完本地代码推送到远程git push在推送之前建议先拉取远程最新代码避免别人在你之前推进了东西git pullgit pull实际上是两个动作的合体先git fetch把远程的最新提交下载到本地再git merge合并进当前分支。我见过不少新人直接git pull然后被合并冲突吓到。这时候可以先用git fetch看一下远程改了什么再决定怎么合并会从容很多。还有一个细节第一次推送新分支时通常要建立本地分支和远程分支的追踪关系git push -u origin feature-login-u表示建立追踪之后直接在分支上敲git push和git pull就行不用再写远程分支名。没建立追踪的话Git 会提示你“当前分支没有上游分支”这时候按照提示补一条命令即可。4. 真实工作流从建仓到合并冲突完整跑一遍讲完零散命令把它们串成一个完整工作流你会发现这个过程其实很顺。4.1 场景一从零开始一个新项目并推到远程假设我在本地创建了一个空文件夹my-project现在想把它变成 Git 仓库并且推到 GitHub 上保存。终端里执行cd my-project git init echo # My Project README.md git add . git commit -m init project这样本地仓库就建好了里面有一条初始提交。然后在 GitHub 网页上手动创建一个新的空仓库创建时不要勾选自动生成 README否则会先有一个提交导致两边历史不一致后面合并会报refusing to merge unrelated histories。创建完成后网页上会给出远程地址。执行git remote add origin gitgithub.com:用户名/my-project.git git push -u origin main打开 GitHub 刷新代码已经上去了。以后每次改动就是三个动作git add .→git commit -m ...→git push。就这么简单。4.2 场景二分支开发与合并冲突解决多人协作时冲突是躲不掉的。很多人第一次看到 CONFLICT 就慌了其实冲突的本质只是两个人改了同一个文件的同一段代码Git 不知道谁说了算让你来做裁判。举个例子。我在main分支上有个文件app.js内容是console.log(Hello World);同事拉了一个分支feat-msg把这一行改成了console.log(Hello Git);同时我在main分支上也改了同一行改成console.log(Hello JavaScript);我执行git merge feat-msgGit 会提示Auto-merging app.js CONFLICT (content): Merge conflict in app.js Automatic merge failed; fix conflicts and then commit the result.打开app.js你会看到这样的内容 HEAD console.log(Hello JavaScript); console.log(Hello Git); feat-msg HEAD到之间是我当前分支的内容到 feat-msg之间是来自feat-msg分支的内容。我需要做的只是把想要的内容保留下来把冲突标记全部删掉。比如保留两边内容console.log(Hello JavaScript); console.log(Hello Git);保存文件后执行git add app.js git commit -m merge feat-msg冲突解决完成。整个过程没有任何魔法就是“编辑文件 → 提交”。说实话我第一次经历时觉得这是个天大的事后来发现它只是日常操作里的一环碰多了就习惯了。预防冲突的小技巧是开发前先把main的最新改动合并到功能分支里或者每天至少git pull一次减少双方改动叠加的概率。分支任务尽量不要拖太久拖得越久合并时冲突面就越大。4.3 补充技能git worktree 并行开发如果你经常需要在多个分支之间切换你会发现来回checkout很烦尤其是当你在一个分支上改到一半突然需要去另一个分支修个紧急 bug。在 Git 2.15 之后git worktree能帮你同时打开多个工作目录。假设项目在~/my-project我已经在feature-a分支上开发现在需要临时去main分支上修一个紧急问题可以给main开一个单独的工作目录git worktree add ../my-project-main main这个命令会在../my-project-main目录下创建一个指向main分支的独立工作区你可以直接在那里改动、提交、推送完全不影响原来的feature-a目录。等修完 bug切换回原目录继续开发。用git worktree list查看当前有哪些工作区。不再需要某个工作区时用git worktree remove ../my-project-main清除。这个命令刚开始用可能觉得没必要一旦手上同时有两三个任务你会发现它特别顺手。5. 常见报错与疑难杂症速查表这部分是很多人最需要的。我把常见的高频报错整理成一个速查表配合对应的排查思路省得每次出错都到处搜。5.1 高频报错对照表报错信息原因解决办法fatal: not a git repository当前目录不在 Git 仓库内用cd进入仓库根目录或在正确目录执行git init无法将“git”项识别为 cmdletGit 没安装或环境变量 PATH 没配置重新安装 Git安装时勾选 PATH 选项新开终端再试Permission denied (publickey)SSH 密钥认证失败检查平台公钥配置、私钥文件是否在、远程地址是否为 SSHfatal: refusing to merge unrelated histories两个仓库没有共同祖先提交确认无误后加--allow-unrelated-histories强制合并warning: LF will be replaced by CRLF换行符转换提醒正常警告可配置core.autocrlf消除detached HEAD当前检出了某个提交而不是分支用git switch 分支名回到正常分支Updates were rejected because the remote contains work远程有新提交本地没有先git pull再git pushnothing to commit, working tree clean暂存区没有新改动先git add或确认当前文件是否真的修改过fatal: not a git repository是我见过的出现频率最高的报错之一绝大多数情况只是终端位置不对。比如你在Desktop目录下敲 Git 命令但仓库里有个子文件夹没有.git目录就会触发这个提示。不要慌cd到仓库根目录再看一眼。5.2 凭证免密与清除账号密码如果你用的是 HTTPS 方式克隆仓库Git 会缓存账号密码。Windows 自带一个凭证管理器第一次输入账号密码后会自动记住Linux/Mac 下 Git 也提供了凭证存储机制。但有时候你需要清除记住的账号密码比如换了账号、或者之前输错了凭据导致一直报权限错误。Windows 下可以在“控制面板 → 凭据管理器 → Windows 凭据”里找到对应的git:https://...条目删掉也可以在终端里执行git credential-manager eraseLinux/Mac 用户可以先看当前凭证存储方式git config --global credential.helper然后清除对应的凭据文件或者用 Git 自带的命令git credential reject按提示输入协议、主机、用户名信息即可。最暴力的方式是删除全局配置里的凭证助手git config --global --unset credential.helper清掉之后下次 push 会重新让你输入账号密码。我个人更推荐直接换 SSH 免密方案一次性配置不仅免密还能彻底告别 HTTPS 凭证的各类疑难杂症。如果你已经用 HTTPS 克隆了仓库也不用重新克隆执行一次远程地址换成 SSH 地址就行git remote set-url origin gitgithub.com:用户名/仓库名.git5.3 图形化工具补充TortoiseGit 与 VS Code如果你真的不想记命令也可以借助图形化工具。Windows 上最经典的是 TortoiseGit就是大家常说的“小乌龟”安装后资源管理器右键菜单会出现 Git 选项提交、拉取、推送都能通过图形界面完成。它的学习曲线很平缓适合刚接触 Git 的人快速建立操作直觉。VS Code 自带了一套非常好用的源代码管理面板平时写代码就在编辑器里左侧栏点一下就能看到当前仓库的改动列表。支持可视化暂存那个加号、提交填写消息后点提交按钮、推送和拉取。用 VS Code 的人完全可以把日常的 Git 操作都留在这里不需要切换终端。不过这里要提醒一句图形化工具能帮你完成操作但不会告诉你操作背后的原理。如果你遇到工具里无法处理的冲突、或者想理解报错信息底层的命令思维迟早还是得补上。我的建议是图形工具负责日常顺手操作终端命令负责救急和排查两条腿走路最稳。6. 用 Git 的安全与卫生习惯必看Git 用久了你会发现很多大坑不是命令不会而是安全意识和卫生习惯缺位。这块内容可能不在你的速成诉求里但我真心希望每个人都看一眼。6.1 .git 目录泄露原理、检测与防护每个 Git 仓库的根目录下都有一个.git文件夹里面保存了提交历史、分支引用、配置信息等所有仓库元数据。这个目录对开发来说是命根子但如果它被暴露给了不该看到的人问题就大了。很多团队把项目目录直接丢到 Nginx 或者 Apache 的静态站点目录里或者把整个项目文件夹打包上传到公开环境结果.git目录也跟着出去了。只要有人猜到了路径通过浏览器直接访问类似http://你的站点/.git/config这样的地址就可能把仓库的配置、甚至完整源码读取出来这类情况在业内被称为“.git 目录泄露”。它的危害在于源码、数据库配置、密钥文件都可能被顺藤摸瓜翻出来。防护手段并不复杂部署时只部署构建产物比如dist、build目录不要把整个开发目录拷到服务器上。服务器配置里直接禁止访问.git路径Nginx 示例配置location ~ /\.git { deny all; }定期自查在浏览器里访问一下你的站点域名加/.git前缀如果能返回内容赶紧处理。不要把包含.git的目录整个压缩成 zip 然后传到公开平台。这个问题的核心逻辑是.git目录是给 Git 软件读的不是给浏览器读的。它一旦出现在公开入口就等于把整份仓库档案摆到了大厅里。我在多个项目上都干过自查这个动作每次确认被拒绝访问才放心。6.2 .gitignore把垃圾文件挡在仓库外新人最容易犯的一个错误是把本地产生的垃圾文件一股脑提交到仓库。比如node_modules目录动辄几百 MB提交进去之后仓库膨胀得厉害队友克隆项目时痛苦不堪。正确的做法是建立一个.gitignore文件把这些不该进仓库的文件排除在外。一个常见的.gitignore模板长这样# 依赖目录 node_modules/ vendor/ # 构建产物 dist/ build/ *.tsbuildinfo # 日志文件 *.log # 操作系统生成的文件 .DS_Store Thumbs.db # IDE 配置 .idea/ .vscode/.gitignore写的是匹配规则node_modules/表示忽略整个目录。要注意的是.gitignore只对没被跟踪的文件生效如果某个文件已经被git add过之后再在.gitignore里写它是不生效的需要先把它从 Git 跟踪里移除git rm -r --cached node_modules--cached表示只删除 Git 索引中的记录不删除本地文件。操作完再提交一次这个大目录就彻底和仓库告别了。6.3 敏感信息不要进仓库这可能是 Git 使用中最容易踩、也最危险的一个坑把数据库密码、密钥、Token 直接写进代码里提交到仓库。我自己就干过这种事测试环境写死了数据库连接串然后顺手 push 到 GitHub等反应过来的时候只能立刻把所有相关凭据作废重配。有人可能会想我把敏感信息从代码里删掉再提交一版不就行了吗不够。因为 Git 的提交历史里永远保留着旧版本只要源码泄露过那些信息就等于裸奔。所以根子上的正确做法是敏感信息一律用环境变量、配置文件且该配置写入.gitignore、或专门的密钥管理服务来管理。代码仓库里只留读取逻辑不留实际值。如果怀疑敏感信息已经被提交最干脆的处理是让这组密钥立即失效重新生成一套。你也可以在团队里利用 Git 钩子做提前拦截比如用 pre-commit 钩子检查是否包含明显的密码关键词。这属于进阶玩法但思路很简单与其事后补救不如在提交之前就把风险拦住。等到问题爆出来处理成本远高于防范成本。最后说点掏心窝的话。我用了这么多年的 Git最大的体会是千万不要把 Git 命令当咒语去背。你只需要抓住三个核心动作——提交commit、分支branch、合并merge再加上每天打开电脑后执行一次git status和git fetch这个小习惯绝大部分日常场景你已经能从容应对。剩下所有记不住的命令恰恰是这本手册存在的意义——遇到问题翻出来查一查查完用一次多用过几次它就成了你自己的东西。工具的价值是让人少犯错而不是给人添堵Git 如此写代码这件事更是如此。